-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #323 from epage/split
fix(tryfn): Split out harness
- Loading branch information
Showing
9 changed files
with
267 additions
and
3 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Change Log | ||
All notable changes to this project will be documented in this file. | ||
|
||
The format is based on [Keep a Changelog](http://keepachangelog.com/) | ||
and this project adheres to [Semantic Versioning](http://semver.org/). | ||
|
||
<!-- next-header --> | ||
## [Unreleased] - ReleaseDate | ||
|
||
<!-- next-url --> | ||
[Unreleased]: https://github.com/assert-rs/trycmd/compare/3e293f6f6167270d85f57a7b59fd94590af6fa97...HEAD |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
[package] | ||
name = "tryfn" | ||
version = "0.1.0" | ||
description = "File-driven snapshot testing for a function" | ||
authors = ["Ed Page <[email protected]>"] | ||
repository = "https://github.com/assert-rs/trycmd.git" | ||
homepage = "https://github.com/assert-rs/trycmd" | ||
documentation = "http://docs.rs/tryfn/" | ||
readme = "README.md" | ||
categories = ["development-tools::testing"] | ||
keywords = ["test", "assert", "snapsjot"] | ||
license.workspace = true | ||
edition.workspace = true | ||
rust-version.workspace = true | ||
include.workspace = true | ||
|
||
[package.metadata.docs.rs] | ||
all-features = true | ||
rustdoc-args = ["--cfg", "docsrs"] | ||
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] | ||
|
||
[package.metadata.release] | ||
pre-release-replacements = [ | ||
{file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
{file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, | ||
{file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
{file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## [Unreleased] - ReleaseDate\n", exactly=1}, | ||
{file="CHANGELOG.md", search="<!-- next-url -->", replace="<!-- next-url -->\n[Unreleased]: https://github.com/assert-rs/trycmd/compare/{{tag_name}}...HEAD", exactly=1}, | ||
] | ||
|
||
[features] | ||
default = ["color-auto", "diff"] | ||
diff = ["snapbox/diff"] | ||
color = ["snapbox/color"] | ||
color-auto = ["snapbox/color-auto"] | ||
|
||
[dependencies] | ||
snapbox = { path = "../snapbox", version = "0.5.9", default-features = false } | ||
libtest-mimic = "0.7.0" | ||
ignore = "0.4" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../LICENSE-APACHE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../LICENSE-MIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# tryfn | ||
|
||
> File-driven snapshot testing for a function | ||
[![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] | ||
![License](https://img.shields.io/crates/l/tryfn.svg) | ||
[![Crates Status](https://img.shields.io/crates/v/tryfn.svg)][Crates.io] | ||
|
||
## License | ||
|
||
Licensed under either of | ||
|
||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>) | ||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>) | ||
|
||
at your option. | ||
|
||
### Contribution | ||
|
||
Unless you explicitly state otherwise, any contribution intentionally | ||
submitted for inclusion in the work by you, as defined in the Apache-2.0 | ||
license, shall be dual licensed as above, without any additional terms or | ||
conditions. | ||
|
||
[Crates.io]: https://crates.io/crates/tryfn | ||
[Documentation]: https://docs.rs/tryfn |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
//! [`Harness`] for discovering test inputs and asserting against snapshot files | ||
//! | ||
//! This is a custom test harness and should be put in its own test binary with | ||
//! [`test.harness = false`](https://doc.rust-lang.org/stable/cargo/reference/cargo-targets.html#the-harness-field). | ||
//! | ||
//! # Examples | ||
//! | ||
//! ```rust,no_run | ||
//! fn some_func(num: usize) -> usize { | ||
//! // ... | ||
//! # 10 | ||
//! } | ||
//! | ||
//! tryfn::Harness::new( | ||
//! "tests/fixtures/invalid", | ||
//! setup, | ||
//! test, | ||
//! ) | ||
//! .select(["tests/cases/*.in"]) | ||
//! .test(); | ||
//! | ||
//! 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"); | ||
//! tryfn::Case { | ||
//! name, | ||
//! fixture: input_path, | ||
//! expected, | ||
//! } | ||
//! } | ||
//! | ||
//! fn test(input_path: &std::path::Path) -> Result<usize, Box<dyn std::error::Error>> { | ||
//! let raw = std::fs::read_to_string(input_path)?; | ||
//! let num = raw.parse::<usize>()?; | ||
//! | ||
//! let actual = some_func(num); | ||
//! | ||
//! Ok(actual) | ||
//! } | ||
//! ``` | ||
use libtest_mimic::Trial; | ||
|
||
pub use snapbox::assert::Action; | ||
pub use snapbox::data::DataFormat; | ||
pub use snapbox::Data; | ||
|
||
/// [`Harness`] for discovering test inputs and asserting against snapshot files | ||
pub struct Harness<S, T> { | ||
root: std::path::PathBuf, | ||
overrides: Option<ignore::overrides::Override>, | ||
setup: S, | ||
test: T, | ||
config: snapbox::Assert, | ||
} | ||
|
||
impl<S, T, I, E> Harness<S, T> | ||
where | ||
I: std::fmt::Display, | ||
E: std::fmt::Display, | ||
S: Fn(std::path::PathBuf) -> Case + Send + Sync + 'static, | ||
T: Fn(&std::path::Path) -> Result<I, E> + Send + Sync + 'static + Clone, | ||
{ | ||
/// Specify where the test scenarios | ||
/// | ||
/// - `input_root`: where to find the files. See [`Self::select`] for restricting what files | ||
/// are considered | ||
/// - `setup`: Given a path, choose the test name and the output location | ||
/// - `test`: Given a path, return the actual output value | ||
pub fn new(input_root: impl Into<std::path::PathBuf>, setup: S, test: T) -> Self { | ||
Self { | ||
root: input_root.into(), | ||
overrides: None, | ||
setup, | ||
test, | ||
config: snapbox::Assert::new().action_env(snapbox::assert::DEFAULT_ACTION_ENV), | ||
} | ||
} | ||
|
||
/// Path patterns for selecting input files | ||
/// | ||
/// This uses gitignore syntax | ||
pub fn select<'p>(mut self, patterns: impl IntoIterator<Item = &'p str>) -> Self { | ||
let mut overrides = ignore::overrides::OverrideBuilder::new(&self.root); | ||
for line in patterns { | ||
overrides.add(line).unwrap(); | ||
} | ||
self.overrides = Some(overrides.build().unwrap()); | ||
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 | ||
/// - Configuring redactions | ||
/// - Override updating environment vaeiable | ||
pub fn with_assert(mut self, config: snapbox::Assert) -> Self { | ||
self.config = config; | ||
self | ||
} | ||
|
||
/// Run tests | ||
pub fn test(self) -> ! { | ||
let mut walk = ignore::WalkBuilder::new(&self.root); | ||
walk.standard_filters(false); | ||
let tests = walk.build().filter_map(|entry| { | ||
let entry = entry.unwrap(); | ||
let is_dir = entry.file_type().map(|f| f.is_dir()).unwrap_or(false); | ||
let path = entry.into_path(); | ||
if let Some(overrides) = &self.overrides { | ||
overrides | ||
.matched(&path, is_dir) | ||
.is_whitelist() | ||
.then_some(path) | ||
} else { | ||
Some(path) | ||
} | ||
}); | ||
|
||
let shared_config = std::sync::Arc::new(self.config); | ||
let tests: Vec<_> = tests | ||
.into_iter() | ||
.map(|path| { | ||
let case = (self.setup)(path); | ||
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())?; | ||
Ok(()) | ||
}) | ||
.with_ignored_flag(shared_config.selected_action() == Action::Ignore) | ||
}) | ||
.collect(); | ||
|
||
let args = libtest_mimic::Arguments::from_args(); | ||
libtest_mimic::run(&args, tests).exit() | ||
} | ||
} | ||
|
||
/// A test case enumerated by the [`Harness`] with data from the `setup` function | ||
/// | ||
/// See [`harness`][crate] for more details | ||
pub struct Case { | ||
/// Display name | ||
pub name: String, | ||
/// Input for the test | ||
pub fixture: std::path::PathBuf, | ||
/// What the actual output should be compared against or updated | ||
pub expected: std::path::PathBuf, | ||
} |