mycotest/
report.rs
1use crate::{Outcome, TestName};
2use core::{fmt, str::FromStr};
3
4pub const TEST_COUNT: &str = "MYCELIUM_TEST_COUNT:";
5pub const START_TEST: &str = "MYCELIUM_TEST_START:";
6pub const FAIL_TEST: &str = "MYCELIUM_TEST_FAIL:";
7pub const PASS_TEST: &str = "MYCELIUM_TEST_PASS:";
8
9#[derive(Debug, Copy, Clone, Eq, PartialEq)]
10pub enum Failure {
11 Fail,
12 Panic,
13 Fault,
14}
15
16#[derive(Debug, Clone, Eq, PartialEq)]
17pub struct ParseError(&'static str);
18
19impl FromStr for Failure {
22 type Err = ParseError;
23 fn from_str(s: &str) -> Result<Self, Self::Err> {
24 match s.trim() {
25 s if s.starts_with("panic") => Ok(Self::Panic),
26 s if s.starts_with("fail") => Ok(Self::Fail),
27 s if s.starts_with("fault") => Ok(Self::Fault),
28 _ => Err(ParseError(
29 "invalid failure kind: expected one of `panic`, `fail`, or `fault`",
30 )),
31 }
32 }
33}
34
35impl Failure {
36 pub fn as_str(&self) -> &'static str {
37 match self {
38 Failure::Fail => "fail",
39 Failure::Fault => "fault",
40 Failure::Panic => "panic",
41 }
42 }
43}
44
45impl fmt::Display for Failure {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 f.pad(self.as_str())
48 }
49}
50
51impl fmt::Display for ParseError {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 fmt::Display::fmt(self.0, f)
54 }
55}
56
57impl<'a> TestName<'a> {
58 pub fn parse_start(line: &'a str) -> Option<Self> {
59 Self::parse(line.strip_prefix(START_TEST)?)
60 }
61
62 #[tracing::instrument(level = "trace")]
63 pub fn parse_outcome(line: &'a str) -> Result<Option<(Self, Outcome)>, ParseError> {
64 let Some(line) = line.strip_prefix("MYCELIUM_TEST_") else {
65 tracing::trace!("not a test outcome");
66 return Ok(None);
67 };
68 tracing::trace!(?line);
69 let (line, result) = if let Some(line) = line.strip_prefix("PASS:") {
70 (line, Ok(()))
71 } else if let Some(line) = line.strip_prefix("FAIL:") {
72 let line = line.trim();
73 tracing::trace!(?line);
74 let failure = line.parse::<Failure>();
75 tracing::trace!(?failure);
76 let failure = failure?;
77 let line = line.strip_prefix(failure.as_str()).unwrap_or(line);
78 (line, Err(failure))
79 } else {
80 tracing::trace!("this is a test start, not an outcome");
81 return Ok(None);
82 };
83 let test = Self::parse(line.trim()).ok_or(ParseError("failed to parse test"));
84 tracing::trace!(?test);
85 Ok(Some((test?, result)))
86 }
87
88 #[cfg(feature = "alloc")]
89 pub fn to_static(self) -> TestName<'static, alloc::string::String> {
90 use alloc::borrow::ToOwned;
91 TestName::new(self.module.to_owned(), self.name.to_owned())
92 }
93
94 fn parse(line: &'a str) -> Option<Self> {
95 let mut line = line.split_whitespace();
96 let module = line.next()?;
97 let name = line.next()?;
98 Some(Self::new(module, name))
99 }
100}