use std::fmt;
pub const HIL_OK_MARKER: &str = "HIL OK";
#[derive(Debug, Clone)]
pub struct HilOutput {
stdout: String,
stderr: String,
exit_code: Option<i32>,
}
impl HilOutput {
#[must_use]
pub fn new(stdout: String, stderr: String, exit_code: Option<i32>) -> Self {
Self {
stdout,
stderr,
exit_code,
}
}
#[must_use]
pub fn passed(&self) -> bool {
self.contains(HIL_OK_MARKER)
}
#[must_use]
pub fn contains(&self, marker: &str) -> bool {
self.stdout.contains(marker) || self.stderr.contains(marker)
}
#[must_use]
pub fn stdout(&self) -> &str {
&self.stdout
}
#[must_use]
pub fn stderr(&self) -> &str {
&self.stderr
}
#[must_use]
pub fn combined(&self) -> String {
format!("{}\n{}", self.stdout, self.stderr)
}
#[must_use]
pub fn exit_code(&self) -> Option<i32> {
self.exit_code
}
}
impl fmt::Display for HilOutput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let code = match self.exit_code {
Some(c) => c.to_string(),
None => "none".to_string(),
};
write!(
f,
"exit={code}\n--- stdout ---\n{}\n--- stderr ---\n{}",
self.stdout, self.stderr
)
}
}
#[macro_export]
macro_rules! assert_hil_ok {
($output:expr) => {
assert!($output.passed(), "HIL test failed.\n{}", $output)
};
($output:expr, $marker:expr) => {
assert!(
$output.contains($marker),
"HIL test failed -- expected {:?}.\n{}",
$marker,
$output,
)
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn passed_detects_marker_in_either_stream() {
assert!(HilOutput::new("boot\nHIL OK\n".into(), String::new(), Some(0)).passed());
assert!(HilOutput::new(String::new(), "HIL OK".into(), Some(0)).passed());
assert!(!HilOutput::new("timeout".into(), String::new(), Some(1)).passed());
}
#[test]
fn contains_and_combined() {
let o = HilOutput::new("hello".into(), "world".into(), Some(0));
assert!(o.contains("hello"));
assert!(o.contains("world"));
assert_eq!(o.combined(), "hello\nworld");
}
#[test]
fn display_includes_code_and_streams() {
let s = HilOutput::new("hi".into(), "there".into(), Some(42)).to_string();
assert!(s.contains("42"));
assert!(s.contains("hi"));
assert!(s.contains("there"));
let none = HilOutput::new(String::new(), String::new(), None).to_string();
assert!(none.contains("exit=none"));
}
#[test]
fn assert_macro_passes() {
let o = HilOutput::new("HIL OK".into(), String::new(), Some(0));
assert_hil_ok!(o);
let o2 = HilOutput::new("CUSTOM".into(), String::new(), Some(0));
assert_hil_ok!(o2, "CUSTOM");
}
}