Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support custom panic messages and add assert_eq! #224

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 41 additions & 7 deletions crates/snapbox/src/assert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
normalize_paths: bool,
substitutions: crate::Substitutions,
pub(crate) palette: crate::report::Palette,
pub(crate) data_format: Option<DataFormat>,
message: Option<std::fmt::Arguments<'a>>,
}

/// # Assertions
impl Assert {
impl<'a> Assert<'a> {
pub fn new() -> Self {
Default::default()
}
Expand All @@ -56,7 +57,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);
}
}

Expand Down Expand Up @@ -87,7 +88,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);
}
}

Expand Down Expand Up @@ -287,11 +288,35 @@ impl Assert {
Ok(())
}
}

fn error_message(
&self,
default_message: &'static str,
) -> crate::report::Styled<ErrorMessage<'a>> {
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,
Expand Down Expand Up @@ -452,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;
Expand All @@ -474,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;
Expand Down Expand Up @@ -505,7 +538,7 @@ impl Assert {
}
}

impl Default for Assert {
impl Default for Assert<'_> {
fn default() -> Self {
Self {
action: Default::default(),
Expand All @@ -514,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())
}
Expand Down
22 changes: 11 additions & 11 deletions crates/snapbox/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<crate::Data>,
timeout: Option<std::time::Duration>,
_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<std::ffi::OsStr>) -> Self {
Self {
cmd: std::process::Command::new(program),
Expand All @@ -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
}
Expand Down Expand Up @@ -275,7 +275,7 @@ impl Command {
}

/// # Run Command
impl Command {
impl<'a> Command<'a> {
/// Run the command and assert on the results
///
/// ```rust
Expand All @@ -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),
Expand Down Expand Up @@ -424,7 +424,7 @@ where
})
}

impl From<std::process::Command> for Command {
impl From<std::process::Command> for Command<'_> {
fn from(cmd: std::process::Command) -> Self {
Self::from_std(cmd)
}
Expand All @@ -435,12 +435,12 @@ impl From<std::process::Command> 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
Expand All @@ -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
}
Expand Down
11 changes: 11 additions & 0 deletions crates/snapbox/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make sense to also add respective macros for assert_matches and assert_eq_path and assert_matches_path ... however I really dislike the names of the latter two, see #225. So I'd rather have us do a breaking rename before adding the new same-named macros.

($left:expr, $right:expr $(,)?) => {
Assert::new().eq($left, $right);
};
($left:expr, $right:expr, $($arg:tt)+) => {
Assert::new().message(format_args!($($arg)+)).eq($left, $right);
};
}
5 changes: 4 additions & 1 deletion src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down