diff --git a/src/engine.rs b/src/engine.rs index 530c09e..fd97dc0 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -4,12 +4,17 @@ use similar::{ChangeTag, TextDiff}; use crate::config::TestConfig; use crate::traits::{Reporter, Runner}; -use crate::{RED, RESET}; +use crate::{Cli, GREEN, GREY, RED, RESET}; pub struct QemuRunner; impl Runner for QemuRunner { - fn run(&mut self, config: &TestConfig, reporter: &dyn Reporter) -> anyhow::Result<()> { + fn run( + &mut self, + config: &TestConfig, + opts: &Cli, + reporter: &dyn Reporter, + ) -> anyhow::Result<()> { reporter.on_test_start(config); let mut p = spawn(&config.command, Some(config.timeout_ms))?; @@ -36,26 +41,40 @@ impl Runner for QemuRunner { anyhow::bail!(e.to_string()); } + let mut verbose_logs = String::new(); + for expect in &test.expect { - if let Err(e) = p.exp_string(expect) { - let fail_msg = match &e { - rexpect::error::Error::Timeout { - expected: exp, got, .. - } => format_diff(exp, got), - rexpect::error::Error::EOF { - expected: exp, got, .. - } => { - format!( - "EOF (OS Crashed)!\n {RED}│{RESET} Expected: '{}'\n {RED}│{RESET} Got: '{}'", - exp, - clean_output(got) - ) + match p.exp_string(expect) { + Ok(got) => { + if opts.verbose { + let clean = clean_output(&got); + let reconstructed = format!("{clean}{GREEN}{expect}{GREY}"); + verbose_logs.push_str(&format!( + " │ Expected: '{}'\n │ Got: '{}'\n", + expect, reconstructed + )); } - _ => e.to_string(), - }; - - reporter.on_step_failure(&test.command, &fail_msg); - anyhow::bail!(e.to_string()); + } + Err(e) => { + let fail_msg = match &e { + rexpect::error::Error::Timeout { + expected: exp, got, .. + } => format_diff(exp, got), + rexpect::error::Error::EOF { + expected: exp, got, .. + } => { + format!( + "EOF (OS Crashed)!\n {RED}│{RESET} Expected: '{}'\n {RED}│{RESET} Got: '{}'", + exp, + clean_output(got) + ) + } + _ => e.to_string(), + }; + + reporter.on_step_failure(&test.command, &fail_msg); + anyhow::bail!(e.to_string()); + } } } @@ -67,7 +86,13 @@ impl Runner for QemuRunner { anyhow::bail!(e.to_string()); } - reporter.on_step_success(&test.command); + let details = if opts.verbose && !verbose_logs.is_empty() { + Some(verbose_logs.trim_end().to_string()) + } else { + None + }; + + reporter.on_step_success(&test.command, details); } let _ = p.process_mut().kill(rexpect::process::Signal::SIGTERM); @@ -78,7 +103,7 @@ impl Runner for QemuRunner { } fn clean_output(raw: &str) -> String { - let mut msg = raw.replace("\r\n", " ↵ ").replace('\n', " ↵ "); + let mut msg = raw.replace("\r\n", " ↵ ").replace(['\n', '\r'], " ↵ "); if let Ok(re) = Regex::new(r"\x1b\[[0-9;]*[mK]|\\u\{1b\}\[[0-9;]*[mK]") { msg = re.replace_all(&msg, "").to_string(); diff --git a/src/lib.rs b/src/lib.rs index 65025c8..acfabd4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,4 +5,19 @@ pub mod traits; const GREEN: &str = "\x1b[32m"; const RED: &str = "\x1b[31m"; +const GREY: &str = "\x1b[90m"; const RESET: &str = "\x1b[0m"; + +use clap::Parser; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub struct Cli { + pub path: String, + + #[arg(long, default_value_t = false)] + pub nui: bool, + + #[arg(long, default_value_t = false)] + pub verbose: bool, +} diff --git a/src/main.rs b/src/main.rs index 7b6f579..685fe16 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ use std::{ }; use walkman::{ + Cli, config::TestConfig, engine::QemuRunner, reporter::{console::ConsoleReporter, tui::TuiReporter}, @@ -12,15 +13,6 @@ use walkman::{ use clap::Parser; -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -struct Cli { - path: String, - - #[arg(long, default_value_t = false)] - nui: bool, -} - fn main() { let cli = Cli::parse(); @@ -71,10 +63,10 @@ fn main() { let result = if cli.nui { let reporter = ConsoleReporter; - runner.run(&config, &reporter) + runner.run(&config, &cli, &reporter) } else { let reporter = TuiReporter::new(&config); - runner.run(&config, &reporter) + runner.run(&config, &cli, &reporter) }; let success = result.is_ok(); diff --git a/src/reporter/console.rs b/src/reporter/console.rs index b997099..228aaaf 100644 --- a/src/reporter/console.rs +++ b/src/reporter/console.rs @@ -1,6 +1,6 @@ use crate::config::TestConfig; use crate::traits::Reporter; -use crate::{GREEN, RED, RESET}; +use crate::{GREEN, GREY, RED, RESET}; pub struct ConsoleReporter; @@ -22,8 +22,11 @@ impl Reporter for ConsoleReporter { print!(" [TEST] Executing: '{}' ... ", step_name); } - fn on_step_success(&self, _step_name: &str) { + fn on_step_success(&self, _step_name: &str, details: Option) { println!("{}SUCCESS{}", GREEN, RESET); + if let Some(msg) = details { + println!("{GREY}{msg}{RESET}"); + } } fn on_step_failure(&self, _step_name: &str, reason: &str) { diff --git a/src/reporter/tui.rs b/src/reporter/tui.rs index c834c95..254dc92 100644 --- a/src/reporter/tui.rs +++ b/src/reporter/tui.rs @@ -12,7 +12,7 @@ use crate::{RED, RESET, config::TestConfig, traits::Reporter}; enum Status { Pending, Running, - Success, + Success(Option), Failed(String), } @@ -131,7 +131,7 @@ impl TuiReporter { ResetColor ); } - Status::Success => { + Status::Success(details) => { let _ = execute!( out, SetForegroundColor(Color::Green), @@ -139,6 +139,16 @@ impl TuiReporter { ResetColor, Print(format!("{}{}\n", prefix, name)) ); + + if let Some(msg) = details { + let _ = execute!( + out, + SetForegroundColor(Color::DarkGrey), + Print(format!("{}\n", msg)), + ResetColor + ); + lines += msg.lines().count() as u16; + } } Status::Running => { let _ = execute!( @@ -213,15 +223,15 @@ impl Reporter for TuiReporter { } fn on_boot_check(&self, msg: &str) { - self.update_boot_status(msg, Status::Success); + self.update_boot_status(msg, Status::Success(None)); } fn on_step_start(&self, step_name: &str) { self.update_shell_status(step_name, Status::Running); } - fn on_step_success(&self, step_name: &str) { - self.update_shell_status(step_name, Status::Success); + fn on_step_success(&self, step_name: &str, details: Option) { + self.update_shell_status(step_name, Status::Success(details)); } fn on_step_failure(&self, step_name: &str, reason: &str) { @@ -293,7 +303,10 @@ mod tests { assert_eq!(reporter.state.borrow().boot_steps[0].1, Status::Running); reporter.on_boot_check("booting"); - assert_eq!(reporter.state.borrow().boot_steps[0].1, Status::Success); + assert_eq!( + reporter.state.borrow().boot_steps[0].1, + Status::Success(None) + ); reporter.on_step_failure("ls", "timeout"); assert_eq!( diff --git a/src/traits.rs b/src/traits.rs index 9784285..0d2de93 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,15 +1,20 @@ -use crate::config::TestConfig; +use crate::{Cli, config::TestConfig}; pub trait Reporter { fn on_test_start(&self, config: &TestConfig); fn on_boot_start(&self, msg: &str); fn on_boot_check(&self, msg: &str); fn on_step_start(&self, step_name: &str); - fn on_step_success(&self, step_name: &str); + fn on_step_success(&self, step_name: &str, details: Option); fn on_step_failure(&self, step_name: &str, reason: &str); fn on_test_end(&self, success: bool); } pub trait Runner { - fn run(&mut self, config: &TestConfig, reporter: &dyn Reporter) -> anyhow::Result<()>; + fn run( + &mut self, + config: &TestConfig, + opts: &Cli, + reporter: &dyn Reporter, + ) -> anyhow::Result<()>; }