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

feat(fn)!: Split Harness out into tryfn #295

Merged
merged 3 commits into from
Apr 23, 2024
Merged
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
11 changes: 9 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions crates/snapbox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ default = ["color-auto", "diff"]

#! Feature Flags

## Simple input/output test harness
harness = ["dep:libtest-mimic", "dep:ignore"]
## Smarter binary file detection
detect-encoding = ["dep:content_inspector"]
## Snapshotting of paths
Expand Down Expand Up @@ -71,9 +69,6 @@ name = "snap-fixture" # For `snapbox`s tests only
normalize-line-endings = "0.3.0"
snapbox-macros = { path = "../snapbox-macros", version = "0.3.8" }

libtest-mimic = { version = "0.7.0", optional = true }
ignore = { version = "0.4", optional = true }

content_inspector = { version = "0.2.4", optional = true }

tempfile = { version = "3.0", optional = true }
Expand Down
8 changes: 7 additions & 1 deletion crates/snapbox/src/assert/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl Assert {
}
}

pub(crate) fn try_eq(
pub fn try_eq(
&self,
expected: crate::Data,
actual: crate::Data,
Expand Down Expand Up @@ -397,6 +397,12 @@ impl Assert {
}
}

impl Assert {
pub fn selected_action(&self) -> Action {
self.action
}
}

impl Default for Assert {
fn default() -> Self {
Self {
Expand Down
36 changes: 0 additions & 36 deletions crates/snapbox/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
//!
//! Testing Functions:
//! - [`assert_eq`][crate::assert_eq()] for quick and dirty snapshotting
//! - [`harness::Harness`] for discovering test inputs and asserting against snapshot files:
//!
//! Testing Commands:
//! - [`cmd::Command`]: Process spawning for testing of non-interactive commands
Expand Down Expand Up @@ -58,38 +57,6 @@
//! .eq(snapbox::file!["help_output_is_clean.txt"], actual);
//! ```
//!
//! [`harness::Harness`]
#![cfg_attr(not(feature = "harness"), doc = " ```rust,ignore")]
#![cfg_attr(feature = "harness", doc = " ```rust,no_run")]
//! snapbox::harness::Harness::new(
//! "tests/fixtures/invalid",
//! setup,
//! test,
//! )
//! .select(["tests/cases/*.in"])
//! .action_env("SNAPSHOTS")
//! .test();
//!
//! fn setup(input_path: std::path::PathBuf) -> snapbox::harness::Case {
//! let name = input_path.file_name().unwrap().to_str().unwrap().to_owned();
//! let expected = snapbox::Data::read_from(&input_path.with_extension("out"), None);
//! snapbox::harness::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 = num + 10;
//!
//! Ok(actual)
//! }
//! ```
//!
//! [trycmd]: https://docs.rs/trycmd

#![cfg_attr(docsrs, feature(doc_auto_cfg))]
Expand All @@ -104,9 +71,6 @@ pub mod path;
pub mod report;
pub mod utils;

#[cfg(feature = "harness")]
pub mod harness;

pub use assert::Assert;
pub use data::Data;
pub use data::IntoData;
Expand Down
11 changes: 11 additions & 0 deletions crates/tryfn/CHANGELOG.md
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
40 changes: 40 additions & 0 deletions crates/tryfn/Cargo.toml
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"
1 change: 1 addition & 0 deletions crates/tryfn/LICENSE-APACHE
1 change: 1 addition & 0 deletions crates/tryfn/LICENSE-MIT
26 changes: 26 additions & 0 deletions crates/tryfn/README.md
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
64 changes: 30 additions & 34 deletions crates/snapbox/src/harness.rs → crates/tryfn/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,23 @@
//! # Examples
//!
//! ```rust,no_run
//! snapbox::harness::Harness::new(
//! fn some_func(num: usize) -> usize {
//! // ...
//! # 10
//! }
//!
//! tryfn::Harness::new(
//! "tests/fixtures/invalid",
//! setup,
//! test,
//! )
//! .select(["tests/cases/*.in"])
//! .action_env("SNAPSHOTS")
//! .test();
//!
//! fn setup(input_path: std::path::PathBuf) -> snapbox::harness::Case {
//! fn setup(input_path: std::path::PathBuf) -> tryfn::Case {
//! let name = input_path.file_name().unwrap().to_str().unwrap().to_owned();
//! let expected = snapbox::Data::read_from(&input_path.with_extension("out"), None);
//! snapbox::harness::Case {
//! let expected = tryfn::Data::read_from(&input_path.with_extension("out"), None);
//! tryfn::Case {
//! name,
//! fixture: input_path,
//! expected,
Expand All @@ -29,26 +33,24 @@
//! let raw = std::fs::read_to_string(input_path)?;
//! let num = raw.parse::<usize>()?;
//!
//! let actual = num + 10;
//! let actual = some_func(num);
//!
//! Ok(actual)
//! }
//! ```

use crate::assert::Action;
use crate::Data;

use libtest_mimic::Trial;

pub use snapbox::data::DataFormat;
pub use snapbox::Data;

/// [`Harness`] for discovering test inputs and asserting against snapshot files
///
/// See [`harness`][crate::harness] for more details
pub struct Harness<S, T, I, E> {
root: std::path::PathBuf,
overrides: Option<ignore::overrides::Override>,
setup: S,
test: T,
config: crate::Assert,
config: snapbox::Assert,
test_output: std::marker::PhantomData<I>,
test_error: std::marker::PhantomData<E>,
}
Expand All @@ -67,29 +69,29 @@ where
/// - `setup`: Given a path, choose the test name and the output location
/// - `test`: Given a path, return the actual output value
///
/// By default [`filters`][crate::filters] are applied, including:
/// By default [`filters`][snapbox::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`][crate::Data::raw] on `expected`.
/// 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<std::path::PathBuf>, setup: S, test: T) -> Self {
Self {
root: input_root.into(),
overrides: None,
setup,
test,
config: crate::Assert::new().action_env(crate::assert::DEFAULT_ACTION_ENV),
config: snapbox::Assert::new().action_env(snapbox::assert::DEFAULT_ACTION_ENV),
test_output: Default::default(),
test_error: Default::default(),
}
}

/// Path patterns for selecting input files
///
/// This used gitignore syntax
/// 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 {
Expand All @@ -99,20 +101,12 @@ 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
pub fn with_assert(mut self, config: crate::Assert) -> Self {
///
/// Includes
/// - Configuring redactions
/// - Override updating environment vaeiable
pub fn with_assert(mut self, config: snapbox::Assert) -> Self {
self.config = config;
self
}
Expand Down Expand Up @@ -149,11 +143,13 @@ where
Trial::test(case.name.clone(), move || {
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(case.expected.clone(), actual, Some(&case.name))?;
Ok(())
})
.with_ignored_flag(shared_config.action == Action::Ignore)
.with_ignored_flag(
shared_config.selected_action() == snapbox::assert::Action::Ignore,
)
})
.collect();

Expand All @@ -162,6 +158,7 @@ where
}
}

/// Function signature for generating a test [`Case`] from a path fixture
pub trait Setup {
fn setup(&self, fixture: std::path::PathBuf) -> Case;
}
Expand All @@ -175,6 +172,7 @@ where
}
}

/// Function signature for running a test [`Case`]
pub trait Test<S, E>
where
S: std::fmt::Display,
Expand All @@ -194,9 +192,7 @@ where
}
}

/// A test case enumerated by the [`Harness`] with data from the `setup` function
///
/// See [`harness`][crate::harness] for more details
/// A test case enumerated by the [`Harness`] with data from the [`Setup`] function
pub struct Case {
/// Display name
pub name: String,
Expand Down
Loading