21
21
//!
22
22
//! fn setup(input_path: std::path::PathBuf) -> tryfn::Case {
23
23
//! 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 );
25
25
//! tryfn::Case {
26
26
//! name,
27
27
//! fixture: input_path,
41
41
42
42
use libtest_mimic:: Trial ;
43
43
44
- pub use snapbox:: assert:: Action ;
45
44
pub use snapbox:: data:: DataFormat ;
46
45
pub use snapbox:: Data ;
47
46
48
47
/// [`Harness`] for discovering test inputs and asserting against snapshot files
49
- pub struct Harness < S , T > {
48
+ pub struct Harness < S , T , I , E > {
50
49
root : std:: path:: PathBuf ,
51
50
overrides : Option < ignore:: overrides:: Override > ,
52
51
setup : S ,
53
52
test : T ,
54
53
config : snapbox:: Assert ,
54
+ test_output : std:: marker:: PhantomData < I > ,
55
+ test_error : std:: marker:: PhantomData < E > ,
55
56
}
56
57
57
- impl < S , T , I , E > Harness < S , T >
58
+ impl < S , T , I , E > Harness < S , T , I , E >
58
59
where
60
+ S : Setup + Send + Sync + ' static ,
61
+ T : Test < I , E > + Clone + Send + Sync + ' static ,
59
62
I : std:: fmt:: Display ,
60
63
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 ,
63
64
{
64
65
/// Specify where the test scenarios
65
66
///
66
67
/// - `input_root`: where to find the files. See [`Self::select`] for restricting what files
67
68
/// are considered
68
69
/// - `setup`: Given a path, choose the test name and the output location
69
70
/// - `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`.
70
80
pub fn new ( input_root : impl Into < std:: path:: PathBuf > , setup : S , test : T ) -> Self {
71
81
Self {
72
82
root : input_root. into ( ) ,
73
83
overrides : None ,
74
84
setup,
75
85
test,
76
86
config : snapbox:: Assert :: new ( ) . action_env ( snapbox:: assert:: DEFAULT_ACTION_ENV ) ,
87
+ test_output : Default :: default ( ) ,
88
+ test_error : Default :: default ( ) ,
77
89
}
78
90
}
79
91
@@ -89,18 +101,6 @@ where
89
101
self
90
102
}
91
103
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
-
104
104
/// Customize the assertion behavior
105
105
///
106
106
/// Includes
@@ -133,18 +133,23 @@ where
133
133
let tests: Vec < _ > = tests
134
134
. into_iter ( )
135
135
. 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
+ ) ;
137
141
let test = self . test . clone ( ) ;
138
142
let config = shared_config. clone ( ) ;
139
143
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 ) ?;
142
145
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 ( ) ) ?;
145
148
Ok ( ( ) )
146
149
} )
147
- . with_ignored_flag ( shared_config. selected_action ( ) == Action :: Ignore )
150
+ . with_ignored_flag (
151
+ shared_config. selected_action ( ) == snapbox:: assert:: Action :: Ignore ,
152
+ )
148
153
} )
149
154
. collect ( ) ;
150
155
@@ -153,14 +158,48 @@ where
153
158
}
154
159
}
155
160
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
159
196
pub struct Case {
160
197
/// Display name
161
198
pub name : String ,
162
199
/// Input for the test
163
200
pub fixture : std:: path:: PathBuf ,
164
201
/// 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 ,
166
205
}
0 commit comments