From 1231dd925e60c53efdba6f67a56c8a640251ff0f Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Fri, 18 Aug 2023 13:27:39 +0200 Subject: [PATCH 1/3] refactor(snapbox): Introduce private Assert::error_message helper fn --- crates/snapbox/src/assert.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/snapbox/src/assert.rs b/crates/snapbox/src/assert.rs index f0267c07..6040ea9c 100644 --- a/crates/snapbox/src/assert.rs +++ b/crates/snapbox/src/assert.rs @@ -56,7 +56,7 @@ impl Assert { fn eq_inner(&self, expected: crate::Data, actual: crate::Data) { let (pattern, actual) = self.normalize_eq(Ok(expected), actual); if let Err(desc) = pattern.and_then(|p| self.try_verify(&p, &actual, None, None)) { - panic!("{}: {}", self.palette.error("Eq failed"), desc); + panic!("{}: {}", self.error_message("Eq failed"), desc); } } @@ -87,7 +87,7 @@ impl Assert { fn matches_inner(&self, pattern: crate::Data, actual: crate::Data) { let (pattern, actual) = self.normalize_match(Ok(pattern), actual); if let Err(desc) = pattern.and_then(|p| self.try_verify(&p, &actual, None, None)) { - panic!("{}: {}", self.palette.error("Match failed"), desc); + panic!("{}: {}", self.error_message("Match failed"), desc); } } @@ -287,6 +287,10 @@ impl Assert { Ok(()) } } + + fn error_message(&self, message: &'static str) -> crate::report::Styled<&str> { + self.palette.error(message) + } } /// # Directory Assertions From 94fb97729cf5348e70a3cd1f98373e86c4736783 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Fri, 18 Aug 2023 13:31:51 +0200 Subject: [PATCH 2/3] feat(snapbox)!: Support custom panic messages Resolves #223. --- crates/snapbox/src/assert.rs | 44 ++++++++++++++++++++++++++++++------ crates/snapbox/src/cmd.rs | 22 +++++++++--------- src/schema.rs | 5 +++- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/crates/snapbox/src/assert.rs b/crates/snapbox/src/assert.rs index 6040ea9c..1694d8a8 100644 --- a/crates/snapbox/src/assert.rs +++ b/crates/snapbox/src/assert.rs @@ -21,17 +21,18 @@ use crate::Action; /// .matches_path(actual, "tests/fixtures/help_output_is_clean.txt"); /// ``` #[derive(Clone, Debug)] -pub struct Assert { +pub struct Assert<'a> { action: Action, action_var: Option, normalize_paths: bool, substitutions: crate::Substitutions, pub(crate) palette: crate::report::Palette, pub(crate) data_format: Option, + message: Option>, } /// # Assertions -impl Assert { +impl<'a> Assert<'a> { pub fn new() -> Self { Default::default() } @@ -288,14 +289,34 @@ impl Assert { } } - fn error_message(&self, message: &'static str) -> crate::report::Styled<&str> { - self.palette.error(message) + fn error_message( + &self, + default_message: &'static str, + ) -> crate::report::Styled> { + self.palette.error(match self.message { + Some(custom_message) => ErrorMessage::Formatted(custom_message), + None => ErrorMessage::Static(default_message), + }) + } +} + +enum ErrorMessage<'a> { + Static(&'static str), + Formatted(std::fmt::Arguments<'a>), +} + +impl std::fmt::Display for ErrorMessage<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ErrorMessage::Static(err) => write!(f, "{err}"), + ErrorMessage::Formatted(err) => write!(f, "{err}"), + } } } /// # Directory Assertions #[cfg(feature = "path")] -impl Assert { +impl Assert<'_> { #[track_caller] pub fn subset_eq( &self, @@ -456,7 +477,7 @@ impl Assert { } /// # Customize Behavior -impl Assert { +impl<'a> Assert<'a> { /// Override the color palette pub fn palette(mut self, palette: crate::report::Palette) -> Self { self.palette = palette; @@ -478,6 +499,14 @@ impl Assert { self } + /// Override the panic message. + /// + /// Currently only supported by `eq`, `eq_path`, `matches` and `matches_path`. + pub fn message(mut self, args: std::fmt::Arguments<'a>) -> Self { + self.message = Some(args); + self + } + /// Override the default [`Substitutions`][crate::Substitutions] pub fn substitutions(mut self, substitutions: crate::Substitutions) -> Self { self.substitutions = substitutions; @@ -509,7 +538,7 @@ impl Assert { } } -impl Default for Assert { +impl Default for Assert<'_> { fn default() -> Self { Self { action: Default::default(), @@ -518,6 +547,7 @@ impl Default for Assert { substitutions: Default::default(), palette: crate::report::Palette::color(), data_format: Default::default(), + message: Default::default(), } .substitutions(crate::Substitutions::with_exe()) } diff --git a/crates/snapbox/src/cmd.rs b/crates/snapbox/src/cmd.rs index 8529852f..50faebbe 100644 --- a/crates/snapbox/src/cmd.rs +++ b/crates/snapbox/src/cmd.rs @@ -5,16 +5,16 @@ use anstream::panic; /// Process spawning for testing of non-interactive commands #[derive(Debug)] -pub struct Command { +pub struct Command<'a> { cmd: std::process::Command, stdin: Option, timeout: Option, _stderr_to_stdout: bool, - config: crate::Assert, + config: crate::Assert<'a>, } /// # Builder API -impl Command { +impl<'a> Command<'a> { pub fn new(program: impl AsRef) -> Self { Self { cmd: std::process::Command::new(program), @@ -37,7 +37,7 @@ impl Command { } /// Customize the assertion behavior - pub fn with_assert(mut self, config: crate::Assert) -> Self { + pub fn with_assert(mut self, config: crate::Assert<'a>) -> Self { self.config = config; self } @@ -275,7 +275,7 @@ impl Command { } /// # Run Command -impl Command { +impl<'a> Command<'a> { /// Run the command and assert on the results /// /// ```rust @@ -288,7 +288,7 @@ impl Command { /// .stdout_eq("42"); /// ``` #[track_caller] - pub fn assert(self) -> OutputAssert { + pub fn assert(self) -> OutputAssert<'a> { let config = self.config.clone(); match self.output() { Ok(output) => OutputAssert::new(output).with_assert(config), @@ -424,7 +424,7 @@ where }) } -impl From for Command { +impl From for Command<'_> { fn from(cmd: std::process::Command) -> Self { Self::from_std(cmd) } @@ -435,12 +435,12 @@ impl From for Command { /// Create an `OutputAssert` through the [`Command::assert`]. /// /// [`Output`]: std::process::Output -pub struct OutputAssert { +pub struct OutputAssert<'a> { output: std::process::Output, - config: crate::Assert, + config: crate::Assert<'a>, } -impl OutputAssert { +impl<'a> OutputAssert<'a> { /// Create an `Assert` for a given [`Output`]. /// /// [`Output`]: std::process::Output @@ -452,7 +452,7 @@ impl OutputAssert { } /// Customize the assertion behavior - pub fn with_assert(mut self, config: crate::Assert) -> Self { + pub fn with_assert(mut self, config: crate::Assert<'a>) -> Self { self.config = config; self } diff --git a/src/schema.rs b/src/schema.rs index 7d7cbfb7..0e6a58f9 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -806,7 +806,10 @@ impl Env { self.remove.extend(other.remove.iter().cloned()); } - pub(crate) fn apply(&self, mut command: snapbox::cmd::Command) -> snapbox::cmd::Command { + pub(crate) fn apply<'a>( + &self, + mut command: snapbox::cmd::Command<'a>, + ) -> snapbox::cmd::Command<'a> { if !self.inherit() { command = command.env_clear(); } From b31c15099d67d78b27c3c7570d4f9a0336ea6891 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Fri, 18 Aug 2023 13:57:00 +0200 Subject: [PATCH 3/3] feat(snapbox): Add assert_eq! macro --- crates/snapbox/src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/snapbox/src/lib.rs b/crates/snapbox/src/lib.rs index 7084c15d..436bb960 100644 --- a/crates/snapbox/src/lib.rs +++ b/crates/snapbox/src/lib.rs @@ -244,3 +244,14 @@ pub fn assert_subset_matches( .action_env(DEFAULT_ACTION_ENV) .subset_matches(pattern_root, actual_root); } + +/// A [`std::assert_eq`] compatible wrapper around the [`Assert`] API. +#[macro_export] +macro_rules! assert_eq { + ($left:expr, $right:expr $(,)?) => { + Assert::new().eq($left, $right); + }; + ($left:expr, $right:expr, $($arg:tt)+) => { + Assert::new().message(format_args!($($arg)+)).eq($left, $right); + }; +}