Skip to content

Commit 95f4564

Browse files
authored
Merge pull request #326 from epage/tryfn
feat(tryfn)!: Future-proof the API a bit
2 parents 029bd07 + 2e3d702 commit 95f4564

File tree

1 file changed

+67
-28
lines changed

1 file changed

+67
-28
lines changed

crates/tryfn/src/lib.rs

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
//!
2222
//! fn setup(input_path: std::path::PathBuf) -> tryfn::Case {
2323
//! let name = input_path.file_name().unwrap().to_str().unwrap().to_owned();
24-
//! let expected = input_path.with_extension("out");
24+
//! let expected = tryfn::Data::read_from(&input_path.with_extension("out"), None);
2525
//! tryfn::Case {
2626
//! name,
2727
//! fixture: input_path,
@@ -41,39 +41,51 @@
4141
4242
use libtest_mimic::Trial;
4343

44-
pub use snapbox::assert::Action;
4544
pub use snapbox::data::DataFormat;
4645
pub use snapbox::Data;
4746

4847
/// [`Harness`] for discovering test inputs and asserting against snapshot files
49-
pub struct Harness<S, T> {
48+
pub struct Harness<S, T, I, E> {
5049
root: std::path::PathBuf,
5150
overrides: Option<ignore::overrides::Override>,
5251
setup: S,
5352
test: T,
5453
config: snapbox::Assert,
54+
test_output: std::marker::PhantomData<I>,
55+
test_error: std::marker::PhantomData<E>,
5556
}
5657

57-
impl<S, T, I, E> Harness<S, T>
58+
impl<S, T, I, E> Harness<S, T, I, E>
5859
where
60+
S: Setup + Send + Sync + 'static,
61+
T: Test<I, E> + Clone + Send + Sync + 'static,
5962
I: std::fmt::Display,
6063
E: std::fmt::Display,
61-
S: Fn(std::path::PathBuf) -> Case + Send + Sync + 'static,
62-
T: Fn(&std::path::Path) -> Result<I, E> + Send + Sync + 'static + Clone,
6364
{
6465
/// Specify where the test scenarios
6566
///
6667
/// - `input_root`: where to find the files. See [`Self::select`] for restricting what files
6768
/// are considered
6869
/// - `setup`: Given a path, choose the test name and the output location
6970
/// - `test`: Given a path, return the actual output value
71+
///
72+
/// By default filters are applied, including:
73+
/// - `...` is a line-wildcard when on a line by itself
74+
/// - `[..]` is a character-wildcard when inside a line
75+
/// - `[EXE]` matches `.exe` on Windows
76+
/// - `\` to `/`
77+
/// - Newlines
78+
///
79+
/// To limit this to newline normalization for text, have [`Setup`] call [`Data::raw`][snapbox::Data::raw] on `expected`.
7080
pub fn new(input_root: impl Into<std::path::PathBuf>, setup: S, test: T) -> Self {
7181
Self {
7282
root: input_root.into(),
7383
overrides: None,
7484
setup,
7585
test,
7686
config: snapbox::Assert::new().action_env(snapbox::assert::DEFAULT_ACTION_ENV),
87+
test_output: Default::default(),
88+
test_error: Default::default(),
7789
}
7890
}
7991

@@ -89,18 +101,6 @@ where
89101
self
90102
}
91103

92-
/// Read the failure action from an environment variable
93-
pub fn action_env(mut self, var_name: &str) -> Self {
94-
self.config = self.config.action_env(var_name);
95-
self
96-
}
97-
98-
/// Override the failure action
99-
pub fn action(mut self, action: Action) -> Self {
100-
self.config = self.config.action(action);
101-
self
102-
}
103-
104104
/// Customize the assertion behavior
105105
///
106106
/// Includes
@@ -133,18 +133,23 @@ where
133133
let tests: Vec<_> = tests
134134
.into_iter()
135135
.map(|path| {
136-
let case = (self.setup)(path);
136+
let case = self.setup.setup(path);
137+
assert!(
138+
case.expected.source().map(|s| s.is_path()).unwrap_or(false),
139+
"`Case::expected` must be from a file"
140+
);
137141
let test = self.test.clone();
138142
let config = shared_config.clone();
139143
Trial::test(case.name.clone(), move || {
140-
let expected = crate::Data::read_from(&case.expected, Some(DataFormat::Text));
141-
let actual = (test)(&case.fixture)?;
144+
let actual = test.run(&case.fixture)?;
142145
let actual = actual.to_string();
143-
let actual = crate::Data::text(actual);
144-
config.try_eq(Some(&case.name), actual, expected.raw())?;
146+
let actual = snapbox::Data::text(actual);
147+
config.try_eq(Some(&case.name), actual, case.expected.clone())?;
145148
Ok(())
146149
})
147-
.with_ignored_flag(shared_config.selected_action() == Action::Ignore)
150+
.with_ignored_flag(
151+
shared_config.selected_action() == snapbox::assert::Action::Ignore,
152+
)
148153
})
149154
.collect();
150155

@@ -153,14 +158,48 @@ where
153158
}
154159
}
155160

156-
/// A test case enumerated by the [`Harness`] with data from the `setup` function
157-
///
158-
/// See [`harness`][crate] for more details
161+
/// Function signature for generating a test [`Case`] from a path fixture
162+
pub trait Setup {
163+
fn setup(&self, fixture: std::path::PathBuf) -> Case;
164+
}
165+
166+
impl<F> Setup for F
167+
where
168+
F: Fn(std::path::PathBuf) -> Case,
169+
{
170+
fn setup(&self, fixture: std::path::PathBuf) -> Case {
171+
(self)(fixture)
172+
}
173+
}
174+
175+
/// Function signature for running a test [`Case`]
176+
pub trait Test<S, E>
177+
where
178+
S: std::fmt::Display,
179+
E: std::fmt::Display,
180+
{
181+
fn run(&self, fixture: &std::path::Path) -> Result<S, E>;
182+
}
183+
184+
impl<F, S, E> Test<S, E> for F
185+
where
186+
F: Fn(&std::path::Path) -> Result<S, E>,
187+
S: std::fmt::Display,
188+
E: std::fmt::Display,
189+
{
190+
fn run(&self, fixture: &std::path::Path) -> Result<S, E> {
191+
(self)(fixture)
192+
}
193+
}
194+
195+
/// A test case enumerated by the [`Harness`] with data from the [`Setup`] function
159196
pub struct Case {
160197
/// Display name
161198
pub name: String,
162199
/// Input for the test
163200
pub fixture: std::path::PathBuf,
164201
/// What the actual output should be compared against or updated
165-
pub expected: std::path::PathBuf,
202+
///
203+
/// Generally derived from `fixture` and loaded with [`Data::read_from`]
204+
pub expected: Data,
166205
}

0 commit comments

Comments
 (0)