libtest_json/
event.rs

1#[derive(Clone, Debug)]
2#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
3#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
4#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
5#[cfg_attr(feature = "serde", serde(tag = "event"))]
6pub enum Event {
7    DiscoverStart(DiscoverStart),
8    DiscoverCase(DiscoverCase),
9    DiscoverComplete(DiscoverComplete),
10    RunStart(RunStart),
11    CaseStart(CaseStart),
12    CaseMessage(CaseMessage),
13    CaseComplete(CaseComplete),
14    RunComplete(RunComplete),
15}
16
17impl Event {
18    #[cfg(feature = "json")]
19    pub fn to_jsonline(&self) -> String {
20        match self {
21            Self::DiscoverStart(event) => event.to_jsonline(),
22            Self::DiscoverCase(event) => event.to_jsonline(),
23            Self::DiscoverComplete(event) => event.to_jsonline(),
24            Self::RunStart(event) => event.to_jsonline(),
25            Self::CaseStart(event) => event.to_jsonline(),
26            Self::CaseMessage(event) => event.to_jsonline(),
27            Self::CaseComplete(event) => event.to_jsonline(),
28            Self::RunComplete(event) => event.to_jsonline(),
29        }
30    }
31}
32
33impl From<DiscoverStart> for Event {
34    fn from(inner: DiscoverStart) -> Self {
35        Self::DiscoverStart(inner)
36    }
37}
38
39impl From<DiscoverCase> for Event {
40    fn from(inner: DiscoverCase) -> Self {
41        Self::DiscoverCase(inner)
42    }
43}
44
45impl From<DiscoverComplete> for Event {
46    fn from(inner: DiscoverComplete) -> Self {
47        Self::DiscoverComplete(inner)
48    }
49}
50
51impl From<RunStart> for Event {
52    fn from(inner: RunStart) -> Self {
53        Self::RunStart(inner)
54    }
55}
56
57impl From<CaseStart> for Event {
58    fn from(inner: CaseStart) -> Self {
59        Self::CaseStart(inner)
60    }
61}
62
63impl From<CaseMessage> for Event {
64    fn from(inner: CaseMessage) -> Self {
65        Self::CaseMessage(inner)
66    }
67}
68
69impl From<CaseComplete> for Event {
70    fn from(inner: CaseComplete) -> Self {
71        Self::CaseComplete(inner)
72    }
73}
74
75impl From<RunComplete> for Event {
76    fn from(inner: RunComplete) -> Self {
77        Self::RunComplete(inner)
78    }
79}
80
81#[derive(Clone, Debug)]
82#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
83#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
85pub struct DiscoverStart {
86    #[cfg_attr(
87        feature = "serde",
88        serde(default, skip_serializing_if = "Option::is_none")
89    )]
90    pub elapsed_s: Option<Elapsed>,
91}
92
93impl DiscoverStart {
94    #[cfg(feature = "json")]
95    pub fn to_jsonline(&self) -> String {
96        use json_write::JsonWrite as _;
97
98        let mut buffer = String::new();
99        buffer.open_object().unwrap();
100
101        buffer.key("event").unwrap();
102        buffer.keyval_sep().unwrap();
103        buffer.value("discover_start").unwrap();
104
105        if let Some(elapsed_s) = self.elapsed_s {
106            buffer.val_sep().unwrap();
107            buffer.key("elapsed_s").unwrap();
108            buffer.keyval_sep().unwrap();
109            buffer.value(String::from(elapsed_s)).unwrap();
110        }
111
112        buffer.close_object().unwrap();
113
114        buffer
115    }
116}
117
118/// A test case was found
119///
120/// The order these are returned in is unspecified and is unrelated to the order they are run in.
121#[derive(Clone, Debug)]
122#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
123#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
124#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
125pub struct DiscoverCase {
126    pub name: String,
127    #[cfg_attr(
128        feature = "serde",
129        serde(default, skip_serializing_if = "RunMode::is_default")
130    )]
131    pub mode: RunMode,
132    /// Whether selected to be run by the user
133    #[cfg_attr(
134        feature = "serde",
135        serde(default = "true_default", skip_serializing_if = "is_true")
136    )]
137    pub selected: bool,
138    #[cfg_attr(
139        feature = "serde",
140        serde(default, skip_serializing_if = "Option::is_none")
141    )]
142    pub elapsed_s: Option<Elapsed>,
143}
144
145impl DiscoverCase {
146    #[cfg(feature = "json")]
147    pub fn to_jsonline(&self) -> String {
148        use json_write::JsonWrite as _;
149
150        let mut buffer = String::new();
151        buffer.open_object().unwrap();
152
153        buffer.key("event").unwrap();
154        buffer.keyval_sep().unwrap();
155        buffer.value("discover_case").unwrap();
156
157        buffer.val_sep().unwrap();
158        buffer.key("name").unwrap();
159        buffer.keyval_sep().unwrap();
160        buffer.value(&self.name).unwrap();
161
162        if !self.mode.is_default() {
163            buffer.val_sep().unwrap();
164            buffer.key("mode").unwrap();
165            buffer.keyval_sep().unwrap();
166            buffer.value(self.mode.as_str()).unwrap();
167        }
168
169        if !self.selected {
170            buffer.val_sep().unwrap();
171            buffer.key("selected").unwrap();
172            buffer.keyval_sep().unwrap();
173            buffer.value(self.selected).unwrap();
174        }
175
176        if let Some(elapsed_s) = self.elapsed_s {
177            buffer.val_sep().unwrap();
178            buffer.key("elapsed_s").unwrap();
179            buffer.keyval_sep().unwrap();
180            buffer.value(String::from(elapsed_s)).unwrap();
181        }
182
183        buffer.close_object().unwrap();
184
185        buffer
186    }
187}
188
189#[derive(Clone, Debug)]
190#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
191#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
192#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
193pub struct DiscoverComplete {
194    #[cfg_attr(
195        feature = "serde",
196        serde(default, skip_serializing_if = "Option::is_none")
197    )]
198    pub elapsed_s: Option<Elapsed>,
199}
200
201impl DiscoverComplete {
202    #[cfg(feature = "json")]
203    pub fn to_jsonline(&self) -> String {
204        use json_write::JsonWrite as _;
205
206        let mut buffer = String::new();
207        buffer.open_object().unwrap();
208
209        buffer.key("event").unwrap();
210        buffer.keyval_sep().unwrap();
211        buffer.value("discover_complete").unwrap();
212
213        if let Some(elapsed_s) = self.elapsed_s {
214            buffer.val_sep().unwrap();
215            buffer.key("elapsed_s").unwrap();
216            buffer.keyval_sep().unwrap();
217            buffer.value(String::from(elapsed_s)).unwrap();
218        }
219
220        buffer.close_object().unwrap();
221
222        buffer
223    }
224}
225
226#[derive(Clone, Debug)]
227#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
228#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
229#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
230pub struct RunStart {
231    #[cfg_attr(
232        feature = "serde",
233        serde(default, skip_serializing_if = "Option::is_none")
234    )]
235    pub elapsed_s: Option<Elapsed>,
236}
237
238impl RunStart {
239    #[cfg(feature = "json")]
240    pub fn to_jsonline(&self) -> String {
241        use json_write::JsonWrite as _;
242
243        let mut buffer = String::new();
244        buffer.open_object().unwrap();
245
246        buffer.key("event").unwrap();
247        buffer.keyval_sep().unwrap();
248        buffer.value("run_start").unwrap();
249
250        if let Some(elapsed_s) = self.elapsed_s {
251            buffer.val_sep().unwrap();
252            buffer.key("elapsed_s").unwrap();
253            buffer.keyval_sep().unwrap();
254            buffer.value(String::from(elapsed_s)).unwrap();
255        }
256
257        buffer.close_object().unwrap();
258
259        buffer
260    }
261}
262
263#[derive(Clone, Debug)]
264#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
265#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
266#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
267pub struct CaseStart {
268    pub name: String,
269    #[cfg_attr(
270        feature = "serde",
271        serde(default, skip_serializing_if = "Option::is_none")
272    )]
273    pub elapsed_s: Option<Elapsed>,
274}
275
276impl CaseStart {
277    #[cfg(feature = "json")]
278    pub fn to_jsonline(&self) -> String {
279        use json_write::JsonWrite as _;
280
281        let mut buffer = String::new();
282        buffer.open_object().unwrap();
283
284        buffer.key("event").unwrap();
285        buffer.keyval_sep().unwrap();
286        buffer.value("case_start").unwrap();
287
288        buffer.val_sep().unwrap();
289        buffer.key("name").unwrap();
290        buffer.keyval_sep().unwrap();
291        buffer.value(&self.name).unwrap();
292
293        if let Some(elapsed_s) = self.elapsed_s {
294            buffer.val_sep().unwrap();
295            buffer.key("elapsed_s").unwrap();
296            buffer.keyval_sep().unwrap();
297            buffer.value(String::from(elapsed_s)).unwrap();
298        }
299
300        buffer.close_object().unwrap();
301
302        buffer
303    }
304}
305
306#[derive(Clone, Debug)]
307#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
308#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
309#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
310pub struct CaseMessage {
311    pub name: String,
312    pub kind: MessageKind,
313    #[cfg_attr(
314        feature = "serde",
315        serde(default, skip_serializing_if = "Option::is_none")
316    )]
317    pub message: Option<String>,
318    #[cfg_attr(
319        feature = "serde",
320        serde(default, skip_serializing_if = "Option::is_none")
321    )]
322    pub elapsed_s: Option<Elapsed>,
323}
324
325impl CaseMessage {
326    #[cfg(feature = "json")]
327    pub fn to_jsonline(&self) -> String {
328        use json_write::JsonWrite as _;
329
330        let mut buffer = String::new();
331        buffer.open_object().unwrap();
332
333        buffer.key("event").unwrap();
334        buffer.keyval_sep().unwrap();
335        buffer.value("case_message").unwrap();
336
337        buffer.val_sep().unwrap();
338        buffer.key("name").unwrap();
339        buffer.keyval_sep().unwrap();
340        buffer.value(&self.name).unwrap();
341
342        buffer.val_sep().unwrap();
343        buffer.key("kind").unwrap();
344        buffer.keyval_sep().unwrap();
345        buffer.value(self.kind.as_str()).unwrap();
346
347        if let Some(message) = &self.message {
348            buffer.val_sep().unwrap();
349            buffer.key("message").unwrap();
350            buffer.keyval_sep().unwrap();
351            buffer.value(message).unwrap();
352        }
353
354        if let Some(elapsed_s) = self.elapsed_s {
355            buffer.val_sep().unwrap();
356            buffer.key("elapsed_s").unwrap();
357            buffer.keyval_sep().unwrap();
358            buffer.value(String::from(elapsed_s)).unwrap();
359        }
360
361        buffer.close_object().unwrap();
362
363        buffer
364    }
365}
366
367#[derive(Clone, Debug)]
368#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
369#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
370#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
371pub struct CaseComplete {
372    pub name: String,
373    #[cfg_attr(
374        feature = "serde",
375        serde(default, skip_serializing_if = "Option::is_none")
376    )]
377    pub elapsed_s: Option<Elapsed>,
378}
379
380impl CaseComplete {
381    #[cfg(feature = "json")]
382    pub fn to_jsonline(&self) -> String {
383        use json_write::JsonWrite as _;
384
385        let mut buffer = String::new();
386        buffer.open_object().unwrap();
387
388        buffer.key("event").unwrap();
389        buffer.keyval_sep().unwrap();
390        buffer.value("case_complete").unwrap();
391
392        buffer.val_sep().unwrap();
393        buffer.key("name").unwrap();
394        buffer.keyval_sep().unwrap();
395        buffer.value(&self.name).unwrap();
396
397        if let Some(elapsed_s) = self.elapsed_s {
398            buffer.val_sep().unwrap();
399            buffer.key("elapsed_s").unwrap();
400            buffer.keyval_sep().unwrap();
401            buffer.value(String::from(elapsed_s)).unwrap();
402        }
403
404        buffer.close_object().unwrap();
405
406        buffer
407    }
408}
409
410#[derive(Clone, Debug)]
411#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
412#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
413#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
414pub struct RunComplete {
415    #[cfg_attr(
416        feature = "serde",
417        serde(default, skip_serializing_if = "Option::is_none")
418    )]
419    pub elapsed_s: Option<Elapsed>,
420}
421
422impl RunComplete {
423    #[cfg(feature = "json")]
424    pub fn to_jsonline(&self) -> String {
425        use json_write::JsonWrite as _;
426
427        let mut buffer = String::new();
428        buffer.open_object().unwrap();
429
430        buffer.key("event").unwrap();
431        buffer.keyval_sep().unwrap();
432        buffer.value("run_complete").unwrap();
433
434        if let Some(elapsed_s) = self.elapsed_s {
435            buffer.val_sep().unwrap();
436            buffer.key("elapsed_s").unwrap();
437            buffer.keyval_sep().unwrap();
438            buffer.value(String::from(elapsed_s)).unwrap();
439        }
440
441        buffer.close_object().unwrap();
442
443        buffer
444    }
445}
446
447#[cfg(feature = "serde")]
448fn true_default() -> bool {
449    true
450}
451
452#[cfg(feature = "serde")]
453fn is_true(yes: &bool) -> bool {
454    *yes
455}
456
457#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
458#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
459#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
460#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
461pub enum RunMode {
462    #[default]
463    Test,
464    Bench,
465}
466
467impl RunMode {
468    pub fn as_str(&self) -> &str {
469        match self {
470            Self::Test => "test",
471            Self::Bench => "bench",
472        }
473    }
474
475    #[cfg(any(feature = "serde", feature = "json"))]
476    fn is_default(&self) -> bool {
477        *self == Default::default()
478    }
479}
480
481#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
482#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
483#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
484#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
485pub enum MessageKind {
486    // Highest precedent items for determining test status last
487    Error,
488    Ignored,
489}
490
491impl MessageKind {
492    pub fn as_str(&self) -> &str {
493        match self {
494            Self::Error => "error",
495            Self::Ignored => "ignored",
496        }
497    }
498}
499
500/// Time elapsed since process start
501#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
502#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
503#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
504#[cfg_attr(feature = "serde", serde(into = "String"))]
505#[cfg_attr(feature = "serde", serde(try_from = "String"))]
506pub struct Elapsed(pub std::time::Duration);
507
508impl std::fmt::Display for Elapsed {
509    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
510        write!(f, "{:.3}s", self.0.as_secs_f64())
511    }
512}
513
514impl std::str::FromStr for Elapsed {
515    type Err = std::num::ParseFloatError;
516
517    fn from_str(src: &str) -> Result<Self, Self::Err> {
518        let secs = src.parse()?;
519        Ok(Elapsed(std::time::Duration::from_secs_f64(secs)))
520    }
521}
522
523impl TryFrom<String> for Elapsed {
524    type Error = std::num::ParseFloatError;
525
526    fn try_from(inner: String) -> Result<Self, Self::Error> {
527        inner.parse()
528    }
529}
530
531impl From<Elapsed> for String {
532    fn from(elapsed: Elapsed) -> Self {
533        elapsed.0.as_secs_f64().to_string()
534    }
535}
536
537#[cfg(feature = "unstable-schema")]
538#[test]
539fn dump_event_schema() {
540    let schema = schemars::schema_for!(Event);
541    let dump = serde_json::to_string_pretty(&schema).unwrap();
542    snapbox::assert_data_eq!(dump, snapbox::file!("../event.schema.json").raw());
543}