Skip to content

Commit

Permalink
Merge pull request #291 from epage/filters
Browse files Browse the repository at this point in the history
fix(assert)!: Merge _eq and _matches with Data::raw
  • Loading branch information
epage authored Apr 22, 2024
2 parents 9511fb4 + 0125168 commit 12ba903
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 210 deletions.
104 changes: 23 additions & 81 deletions crates/snapbox/src/assert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::Action;
/// # use snapbox::Assert;
/// # use snapbox::file;
/// let actual = "something";
/// Assert::new().matches(file!["output.txt"], actual);
/// Assert::new().eq(file!["output.txt"], actual);
/// ```
#[derive(Clone, Debug)]
pub struct Assert {
Expand All @@ -37,12 +37,19 @@ impl Assert {

/// Check if a value is the same as an expected value
///
/// When the content is text, newlines are normalized.
/// By default [`filters`][crate::filters] are applied, including:
/// - `...` is a line-wildcard when on a line by itself
/// - `[..]` is a character-wildcard when inside a line
/// - `[EXE]` matches `.exe` on Windows
/// - `\` to `/`
/// - Newlines
///
/// To limit this to newline normalization for text, call [`Data::raw`][crate::Data::raw] on `expected`.
///
/// ```rust
/// # use snapbox::Assert;
/// let actual = "something";
/// let expected = "something";
/// let expected = "so[..]g";
/// Assert::new().eq(expected, actual);
/// ```
///
Expand Down Expand Up @@ -78,98 +85,33 @@ impl Assert {
Action::Ignore | Action::Verify | Action::Overwrite => {}
}

let (expected, actual) = self.normalize_eq(expected, actual);
let (expected, actual) = self.normalize(expected, actual);

self.do_action(expected, actual, actual_name)
}

/// Check if a value matches a pattern
///
/// Pattern syntax:
/// - `...` is a line-wildcard when on a line by itself
/// - `[..]` is a character-wildcard when inside a line
/// - `[EXE]` matches `.exe` on Windows
///
/// Normalization:
/// - Newlines
/// - `\` to `/`
///
/// ```rust
/// # use snapbox::Assert;
/// let actual = "something";
/// let expected = "so[..]g";
/// Assert::new().matches(expected, actual);
/// ```
///
/// Can combine this with [`file!`][crate::file]
/// ```rust,no_run
/// # use snapbox::Assert;
/// # use snapbox::file;
/// let actual = "something";
/// Assert::new().matches(file!["output.txt"], actual);
/// ```
#[track_caller]
pub fn matches(&self, pattern: impl Into<crate::Data>, actual: impl Into<crate::Data>) {
let pattern = pattern.into();
let actual = actual.into();
if let Err(err) = self.try_matches(pattern, actual, Some(&"In-memory")) {
err.panic();
}
}

pub(crate) fn try_matches(
pub(crate) fn normalize(
&self,
pattern: crate::Data,
actual: crate::Data,
actual_name: Option<&dyn std::fmt::Display>,
) -> Result<(), crate::Error> {
if pattern.source().is_none() && actual.source().is_some() {
panic!("received `(actual, expected)`, expected `(expected, actual)`");
}
match self.action {
Action::Skip => {
return Ok(());
}
Action::Ignore | Action::Verify | Action::Overwrite => {}
}

let (expected, actual) = self.normalize_match(pattern, actual);

self.do_action(expected, actual, actual_name)
}

pub(crate) fn normalize_eq(
&self,
expected: crate::Data,
mut expected: crate::Data,
mut actual: crate::Data,
) -> (crate::Data, crate::Data) {
let expected = FilterNewlines.filter(expected);
// On `expected` being an error, make a best guess
let format = expected.intended_format();

actual = FilterNewlines.filter(actual.coerce_to(format));

(expected, actual)
}
if expected.filters.is_newlines_set() {
expected = FilterNewlines.filter(expected);
}

pub(crate) fn normalize_match(
&self,
expected: crate::Data,
mut actual: crate::Data,
) -> (crate::Data, crate::Data) {
let expected = FilterNewlines.filter(expected);
// On `expected` being an error, make a best guess
let format = expected.intended_format();
actual = actual.coerce_to(format);

if self.normalize_paths {
if self.normalize_paths && expected.filters.is_paths_set() {
actual = FilterPaths.filter(actual);
}
// Always normalize new lines
actual = FilterNewlines.filter(actual);

// If expected is not an error normalize matches
actual = FilterRedactions::new(&self.substitutions, &expected).filter(actual);
if expected.filters.is_newlines_set() {
actual = FilterNewlines.filter(actual);
}
if expected.filters.is_redaction_set() {
actual = FilterRedactions::new(&self.substitutions, &expected).filter(actual);
}

(expected, actual)
}
Expand Down
102 changes: 19 additions & 83 deletions crates/snapbox/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,15 @@ impl OutputAssert {

/// Ensure the command wrote the expected data to `stdout`.
///
/// By default [`filters`][crate::filters] are applied, including:
/// - `...` is a line-wildcard when on a line by itself
/// - `[..]` is a character-wildcard when inside a line
/// - `[EXE]` matches `.exe` on Windows
/// - `\` to `/`
/// - Newlines
///
/// To limit this to newline normalization for text, call [`Data::raw`][crate::Data::raw] on `expected`.
///
/// ```rust,no_run
/// use snapbox::cmd::Command;
/// use snapbox::cmd::cargo_bin;
Expand All @@ -596,7 +605,7 @@ impl OutputAssert {
/// .env("stdout", "hello")
/// .env("stderr", "world")
/// .assert()
/// .stdout_eq("hello");
/// .stdout_eq("he[..]o");
/// ```
///
/// Can combine this with [`file!`][crate::file]
Expand Down Expand Up @@ -627,48 +636,16 @@ impl OutputAssert {
self
}

/// Ensure the command wrote the expected data to `stdout`.
///
/// ```rust,no_run
/// use snapbox::cmd::Command;
/// use snapbox::cmd::cargo_bin;
///
/// let assert = Command::new(cargo_bin("snap-fixture"))
/// .env("stdout", "hello")
/// .env("stderr", "world")
/// .assert()
/// .stdout_matches("he[..]o");
/// ```
/// Ensure the command wrote the expected data to `stderr`.
///
/// Can combine this with [`file!`][crate::file]
/// ```rust,no_run
/// use snapbox::cmd::Command;
/// use snapbox::cmd::cargo_bin;
/// use snapbox::file;
/// By default [`filters`][crate::filters] are applied, including:
/// - `...` is a line-wildcard when on a line by itself
/// - `[..]` is a character-wildcard when inside a line
/// - `[EXE]` matches `.exe` on Windows
/// - `\` to `/`
/// - Newlines
///
/// let assert = Command::new(cargo_bin("snap-fixture"))
/// .env("stdout", "hello")
/// .env("stderr", "world")
/// .assert()
/// .stdout_matches(file!["stdout.log"]);
/// ```
#[track_caller]
pub fn stdout_matches(self, expected: impl Into<crate::Data>) -> Self {
let expected = expected.into();
self.stdout_matches_inner(expected)
}

#[track_caller]
fn stdout_matches_inner(self, expected: crate::Data) -> Self {
let actual = crate::Data::from(self.output.stdout.as_slice());
if let Err(err) = self.config.try_matches(expected, actual, Some(&"stdout")) {
err.panic();
}

self
}

/// Ensure the command wrote the expected data to `stderr`.
/// To limit this to newline normalization for text, call [`Data::raw`][crate::Data::raw] on `expected`.
///
/// ```rust,no_run
/// use snapbox::cmd::Command;
Expand All @@ -678,7 +655,7 @@ impl OutputAssert {
/// .env("stdout", "hello")
/// .env("stderr", "world")
/// .assert()
/// .stderr_eq("world");
/// .stderr_eq("wo[..]d");
/// ```
///
/// Can combine this with [`file!`][crate::file]
Expand Down Expand Up @@ -709,47 +686,6 @@ impl OutputAssert {
self
}

/// Ensure the command wrote the expected data to `stderr`.
///
/// ```rust,no_run
/// use snapbox::cmd::Command;
/// use snapbox::cmd::cargo_bin;
///
/// let assert = Command::new(cargo_bin("snap-fixture"))
/// .env("stdout", "hello")
/// .env("stderr", "world")
/// .assert()
/// .stderr_matches("wo[..]d");
/// ```
///
/// Can combine this with [`file!`][crate::file]
/// ```rust,no_run
/// use snapbox::cmd::Command;
/// use snapbox::cmd::cargo_bin;
/// use snapbox::file;
///
/// let assert = Command::new(cargo_bin("snap-fixture"))
/// .env("stdout", "hello")
/// .env("stderr", "world")
/// .assert()
/// .stderr_matches(file!["stderr.log"]);
/// ```
#[track_caller]
pub fn stderr_matches(self, expected: impl Into<crate::Data>) -> Self {
let expected = expected.into();
self.stderr_matches_inner(expected)
}

#[track_caller]
fn stderr_matches_inner(self, expected: crate::Data) -> Self {
let actual = crate::Data::from(self.output.stderr.as_slice());
if let Err(err) = self.config.try_matches(expected, actual, Some(&"stderr")) {
err.panic();
}

self
}

fn write_stdout(&self, writer: &mut dyn std::fmt::Write) -> Result<(), std::fmt::Error> {
if !self.output.stdout.is_empty() {
writeln!(writer, "stdout:")?;
Expand Down
56 changes: 56 additions & 0 deletions crates/snapbox/src/data/filters.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
pub(crate) struct FilterSet {
flags: usize,
}

impl FilterSet {
pub(crate) fn new() -> Self {
Self::empty().redactions().newlines().paths()
}

pub(crate) const fn empty() -> Self {
Self { flags: 0 }
}

pub(crate) fn redactions(mut self) -> Self {
self.set(Self::REDACTIONS);
self
}

pub(crate) fn newlines(mut self) -> Self {
self.set(Self::NEWLINES);
self
}

pub(crate) fn paths(mut self) -> Self {
self.set(Self::PATHS);
self
}

pub(crate) const fn is_redaction_set(&self) -> bool {
self.is_set(Self::REDACTIONS)
}

pub(crate) const fn is_newlines_set(&self) -> bool {
self.is_set(Self::NEWLINES)
}

pub(crate) const fn is_paths_set(&self) -> bool {
self.is_set(Self::PATHS)
}
}

impl FilterSet {
const REDACTIONS: usize = 1 << 0;
const NEWLINES: usize = 1 << 1;
const PATHS: usize = 1 << 2;

fn set(&mut self, flag: usize) -> &mut Self {
self.flags |= flag;
self
}

const fn is_set(&self, flag: usize) -> bool {
self.flags & flag != 0
}
}
Loading

0 comments on commit 12ba903

Please sign in to comment.