]> git.proxmox.com Git - rustc.git/blame - vendor/snapbox/src/lib.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / vendor / snapbox / src / lib.rs
CommitLineData
0a29b90c
FG
1//! # Snapshot testing toolbox
2//!
3//! > When you have to treat your tests like pets, instead of [cattle][trycmd]
4//!
5//! `snapbox` is a snapshot-testing toolbox that is ready to use for verifying output from
6//! - Function return values
7//! - CLI stdout/stderr
8//! - Filesystem changes
9//!
10//! It is also flexible enough to build your own test harness like [trycmd](https://crates.io/crates/trycmd).
11//!
12//! ## Which tool is right
13//!
14//! - [cram](https://bitheap.org/cram/): End-to-end CLI snapshotting agnostic of any programming language
15//! - [trycmd](https://crates.io/crates/trycmd): For running a lot of blunt tests (limited test predicates)
16//! - Particular attention is given to allow the test data to be pulled into documentation, like
17//! with [mdbook](https://rust-lang.github.io/mdBook/)
18//! - `snapbox`: When you want something like `trycmd` in one off
19//! cases or you need to customize `trycmd`s behavior.
20//! - [assert_cmd](https://crates.io/crates/assert_cmd) +
21//! [assert_fs](https://crates.io/crates/assert_fs): Test cases follow a certain pattern but
22//! special attention is needed in how to verify the results.
23//! - Hand-written test cases: for peculiar circumstances
24//!
25//! ## Getting Started
26//!
27//! Testing Functions:
28//! - [`assert_eq`][crate::assert_eq] and [`assert_matches`] for reusing diffing / pattern matching for non-snapshot testing
29//! - [`assert_eq_path`][crate::assert_eq_path] and [`assert_matches_path`] for one-off assertions with the snapshot stored in a file
30//! - [`harness::Harness`] for discovering test inputs and asserting against snapshot files:
31//!
32//! Testing Commands:
33//! - [`cmd::Command`]: Process spawning for testing of non-interactive commands
34//! - [`cmd::OutputAssert`]: Assert the state of a [`Command`][cmd::Command]'s
35//! [`Output`][std::process::Output].
36//!
37//! Testing Filesystem Interactions:
38//! - [`path::PathFixture`]: Working directory for tests
39//! - [`Assert`]: Diff a directory against files present in a pattern directory
40//!
41//! You can also build your own version of these with the lower-level building blocks these are
42//! made of.
43//!
44#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
45//!
46//! # Examples
47//!
48//! [`assert_matches`]
49//! ```rust
50//! snapbox::assert_matches("Hello [..] people!", "Hello many people!");
51//! ```
52//!
53//! [`Assert`]
54//! ```rust,no_run
55//! let actual = "...";
56//! let expected_path = "tests/fixtures/help_output_is_clean.txt";
57//! snapbox::Assert::new()
58//! .action_env("SNAPSHOTS")
59//! .matches_path(expected_path, actual);
60//! ```
61//!
62//! [`harness::Harness`]
63#![cfg_attr(not(feature = "harness"), doc = " ```rust,ignore")]
64#![cfg_attr(feature = "harness", doc = " ```rust,no_run")]
65//! snapbox::harness::Harness::new(
66//! "tests/fixtures/invalid",
67//! setup,
68//! test,
69//! )
70//! .select(["tests/cases/*.in"])
71//! .action_env("SNAPSHOTS")
72//! .test();
73//!
74//! fn setup(input_path: std::path::PathBuf) -> snapbox::harness::Case {
75//! let name = input_path.file_name().unwrap().to_str().unwrap().to_owned();
76//! let expected = input_path.with_extension("out");
77//! snapbox::harness::Case {
78//! name,
79//! fixture: input_path,
80//! expected,
81//! }
82//! }
83//!
84//! fn test(input_path: &std::path::Path) -> Result<usize, Box<dyn std::error::Error>> {
85//! let raw = std::fs::read_to_string(input_path)?;
86//! let num = raw.parse::<usize>()?;
87//!
88//! let actual = num + 10;
89//!
90//! Ok(actual)
91//! }
92//! ```
93//!
94//! [trycmd]: https://docs.rs/trycmd
95
96#![cfg_attr(docsrs, feature(doc_auto_cfg))]
97
98mod action;
99mod assert;
100mod data;
101mod error;
102mod substitutions;
103
104pub mod cmd;
105pub mod path;
106pub mod report;
107pub mod utils;
108
109#[cfg(feature = "harness")]
110pub mod harness;
111
112pub use action::Action;
113pub use action::DEFAULT_ACTION_ENV;
114pub use assert::Assert;
115pub use data::Data;
116pub use data::DataFormat;
117pub use data::{Normalize, NormalizeMatches, NormalizeNewlines, NormalizePaths};
118pub use error::Error;
119pub use snapbox_macros::debug;
120pub use substitutions::Substitutions;
121
122pub type Result<T, E = Error> = std::result::Result<T, E>;
123
124/// Check if a value is the same as an expected value
125///
126/// When the content is text, newlines are normalized.
127///
128/// ```rust
129/// let output = "something";
130/// let expected = "something";
781aab86 131/// snapbox::assert_eq(expected, output);
0a29b90c
FG
132/// ```
133#[track_caller]
134pub fn assert_eq(expected: impl Into<crate::Data>, actual: impl Into<crate::Data>) {
135 Assert::new().eq(expected, actual);
136}
137
138/// Check if a value matches a pattern
139///
140/// Pattern syntax:
141/// - `...` is a line-wildcard when on a line by itself
142/// - `[..]` is a character-wildcard when inside a line
143/// - `[EXE]` matches `.exe` on Windows
144///
145/// Normalization:
146/// - Newlines
147/// - `\` to `/`
148///
149/// ```rust
150/// let output = "something";
151/// let expected = "so[..]g";
152/// snapbox::assert_matches(expected, output);
153/// ```
154#[track_caller]
155pub fn assert_matches(pattern: impl Into<crate::Data>, actual: impl Into<crate::Data>) {
156 Assert::new().matches(pattern, actual);
157}
158
159/// Check if a value matches the content of a file
160///
161/// When the content is text, newlines are normalized.
162///
163/// ```rust,no_run
164/// let output = "something";
165/// let expected_path = "tests/snapshots/output.txt";
166/// snapbox::assert_eq_path(expected_path, output);
167/// ```
168#[track_caller]
169pub fn assert_eq_path(expected_path: impl AsRef<std::path::Path>, actual: impl Into<crate::Data>) {
170 Assert::new()
171 .action_env(DEFAULT_ACTION_ENV)
172 .eq_path(expected_path, actual);
173}
174
175/// Check if a value matches the pattern in a file
176///
177/// Pattern syntax:
178/// - `...` is a line-wildcard when on a line by itself
179/// - `[..]` is a character-wildcard when inside a line
180/// - `[EXE]` matches `.exe` on Windows
181///
182/// Normalization:
183/// - Newlines
184/// - `\` to `/`
185///
186/// ```rust,no_run
187/// let output = "something";
188/// let expected_path = "tests/snapshots/output.txt";
189/// snapbox::assert_matches_path(expected_path, output);
190/// ```
191#[track_caller]
192pub fn assert_matches_path(
193 pattern_path: impl AsRef<std::path::Path>,
194 actual: impl Into<crate::Data>,
195) {
196 Assert::new()
197 .action_env(DEFAULT_ACTION_ENV)
198 .matches_path(pattern_path, actual);
199}
200
201/// Check if a path matches the content of another path, recursively
202///
203/// When the content is text, newlines are normalized.
204///
205/// ```rust,no_run
206/// let output_root = "...";
207/// let expected_root = "tests/snapshots/output.txt";
208/// snapbox::assert_subset_eq(expected_root, output_root);
209/// ```
210#[cfg(feature = "path")]
211#[track_caller]
212pub fn assert_subset_eq(
213 expected_root: impl Into<std::path::PathBuf>,
214 actual_root: impl Into<std::path::PathBuf>,
215) {
216 Assert::new()
217 .action_env(DEFAULT_ACTION_ENV)
218 .subset_eq(expected_root, actual_root);
219}
220
221/// Check if a path matches the pattern of another path, recursively
222///
223/// Pattern syntax:
224/// - `...` is a line-wildcard when on a line by itself
225/// - `[..]` is a character-wildcard when inside a line
226/// - `[EXE]` matches `.exe` on Windows
227///
228/// Normalization:
229/// - Newlines
230/// - `\` to `/`
231///
232/// ```rust,no_run
233/// let output_root = "...";
234/// let expected_root = "tests/snapshots/output.txt";
235/// snapbox::assert_subset_matches(expected_root, output_root);
236/// ```
237#[cfg(feature = "path")]
238#[track_caller]
239pub fn assert_subset_matches(
240 pattern_root: impl Into<std::path::PathBuf>,
241 actual_root: impl Into<std::path::PathBuf>,
242) {
243 Assert::new()
244 .action_env(DEFAULT_ACTION_ENV)
245 .subset_matches(pattern_root, actual_root);
246}