From 7cfaf0a8c18c1edeca96f26e45ca2dd0e7102f3a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 17 May 2024 15:52:48 -0500 Subject: [PATCH 1/3] fix(tryfn): Remove deprecated fn's Cherry pick a30d3bf4d72aef572a1794d3eb002fd518a56b2b (#295) --- crates/tryfn/src/lib.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/crates/tryfn/src/lib.rs b/crates/tryfn/src/lib.rs index 0cc04857..1bcbd7ba 100644 --- a/crates/tryfn/src/lib.rs +++ b/crates/tryfn/src/lib.rs @@ -41,7 +41,6 @@ use libtest_mimic::Trial; -pub use snapbox::assert::Action; pub use snapbox::data::DataFormat; pub use snapbox::Data; @@ -89,18 +88,6 @@ where self } - /// Read the failure action from an environment variable - pub fn action_env(mut self, var_name: &str) -> Self { - self.config = self.config.action_env(var_name); - self - } - - /// Override the failure action - pub fn action(mut self, action: Action) -> Self { - self.config = self.config.action(action); - self - } - /// Customize the assertion behavior /// /// Includes @@ -144,7 +131,9 @@ where config.try_eq(Some(&case.name), actual, expected.raw())?; Ok(()) }) - .with_ignored_flag(shared_config.selected_action() == Action::Ignore) + .with_ignored_flag( + shared_config.selected_action() == snapbox::assert::Action::Ignore, + ) }) .collect(); From 25f412d0d693929d56b8c62305183ea97635f662 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 17 May 2024 15:57:35 -0500 Subject: [PATCH 2/3] feat(tryfn)!: Allow test case to define format and filters This builds on and mirrors the extensibility work that was done in `snapbox` where `Data::read_from` and `Data::raw` can be used to override default behavior. Cherry pick 30a9b788f7b8d8a9110f8aacb377046c36723722 (#274) Cherry pick 94e87bae967240642ef964aaa92f3df73eebbbb7 (#283) --- crates/tryfn/src/lib.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/tryfn/src/lib.rs b/crates/tryfn/src/lib.rs index 1bcbd7ba..b2cb7927 100644 --- a/crates/tryfn/src/lib.rs +++ b/crates/tryfn/src/lib.rs @@ -21,7 +21,7 @@ //! //! fn setup(input_path: std::path::PathBuf) -> tryfn::Case { //! let name = input_path.file_name().unwrap().to_str().unwrap().to_owned(); -//! let expected = input_path.with_extension("out"); +//! let expected = tryfn::Data::read_from(&input_path.with_extension("out"), None); //! tryfn::Case { //! name, //! fixture: input_path, @@ -121,14 +121,17 @@ where .into_iter() .map(|path| { let case = (self.setup)(path); + assert!( + case.expected.source().map(|s| s.is_path()).unwrap_or(false), + "`Case::expected` must be from a file" + ); let test = self.test.clone(); let config = shared_config.clone(); Trial::test(case.name.clone(), move || { - let expected = crate::Data::read_from(&case.expected, Some(DataFormat::Text)); let actual = (test)(&case.fixture)?; let actual = actual.to_string(); let actual = crate::Data::text(actual); - config.try_eq(Some(&case.name), actual, expected.raw())?; + config.try_eq(Some(&case.name), actual, case.expected.clone())?; Ok(()) }) .with_ignored_flag( @@ -151,5 +154,7 @@ pub struct Case { /// Input for the test pub fixture: std::path::PathBuf, /// What the actual output should be compared against or updated - pub expected: std::path::PathBuf, + /// + /// Generally derived from `fixture` and loaded with [`Data::read_from`] + pub expected: Data, } From 2e3d70274afad94d01daa7f6004b82d164e4a6d5 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 17 May 2024 15:59:51 -0500 Subject: [PATCH 3/3] fix(harness): Generalize function parameters with traits Cherry pick d935e0b24a064c804855460367ab3f59492c8f11 (#283) --- crates/tryfn/src/lib.rs | 65 ++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/crates/tryfn/src/lib.rs b/crates/tryfn/src/lib.rs index b2cb7927..b04ba4b6 100644 --- a/crates/tryfn/src/lib.rs +++ b/crates/tryfn/src/lib.rs @@ -45,20 +45,22 @@ pub use snapbox::data::DataFormat; pub use snapbox::Data; /// [`Harness`] for discovering test inputs and asserting against snapshot files -pub struct Harness { +pub struct Harness { root: std::path::PathBuf, overrides: Option, setup: S, test: T, config: snapbox::Assert, + test_output: std::marker::PhantomData, + test_error: std::marker::PhantomData, } -impl Harness +impl Harness where + S: Setup + Send + Sync + 'static, + T: Test + Clone + Send + Sync + 'static, I: std::fmt::Display, E: std::fmt::Display, - S: Fn(std::path::PathBuf) -> Case + Send + Sync + 'static, - T: Fn(&std::path::Path) -> Result + Send + Sync + 'static + Clone, { /// Specify where the test scenarios /// @@ -66,6 +68,15 @@ where /// are considered /// - `setup`: Given a path, choose the test name and the output location /// - `test`: Given a path, return the actual output value + /// + /// By default 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, have [`Setup`] call [`Data::raw`][snapbox::Data::raw] on `expected`. pub fn new(input_root: impl Into, setup: S, test: T) -> Self { Self { root: input_root.into(), @@ -73,6 +84,8 @@ where setup, test, config: snapbox::Assert::new().action_env(snapbox::assert::DEFAULT_ACTION_ENV), + test_output: Default::default(), + test_error: Default::default(), } } @@ -120,7 +133,7 @@ where let tests: Vec<_> = tests .into_iter() .map(|path| { - let case = (self.setup)(path); + let case = self.setup.setup(path); assert!( case.expected.source().map(|s| s.is_path()).unwrap_or(false), "`Case::expected` must be from a file" @@ -128,9 +141,9 @@ where let test = self.test.clone(); let config = shared_config.clone(); Trial::test(case.name.clone(), move || { - let actual = (test)(&case.fixture)?; + let actual = test.run(&case.fixture)?; let actual = actual.to_string(); - let actual = crate::Data::text(actual); + let actual = snapbox::Data::text(actual); config.try_eq(Some(&case.name), actual, case.expected.clone())?; Ok(()) }) @@ -145,9 +158,41 @@ where } } -/// A test case enumerated by the [`Harness`] with data from the `setup` function -/// -/// See [`harness`][crate] for more details +/// Function signature for generating a test [`Case`] from a path fixture +pub trait Setup { + fn setup(&self, fixture: std::path::PathBuf) -> Case; +} + +impl Setup for F +where + F: Fn(std::path::PathBuf) -> Case, +{ + fn setup(&self, fixture: std::path::PathBuf) -> Case { + (self)(fixture) + } +} + +/// Function signature for running a test [`Case`] +pub trait Test +where + S: std::fmt::Display, + E: std::fmt::Display, +{ + fn run(&self, fixture: &std::path::Path) -> Result; +} + +impl Test for F +where + F: Fn(&std::path::Path) -> Result, + S: std::fmt::Display, + E: std::fmt::Display, +{ + fn run(&self, fixture: &std::path::Path) -> Result { + (self)(fixture) + } +} + +/// A test case enumerated by the [`Harness`] with data from the [`Setup`] function pub struct Case { /// Display name pub name: String,