1
1
// Copyright (c) The datatest-stable Contributors
2
2
// SPDX-License-Identifier: MIT OR Apache-2.0
3
3
4
- use camino:: { Utf8Path , Utf8PathBuf } ;
4
+ use camino:: { Utf8Component , Utf8Path , Utf8PathBuf } ;
5
5
6
6
#[ derive( Debug ) ]
7
7
#[ doc( hidden) ]
8
8
pub enum DataSource {
9
+ // This is relative to the crate root, and stored with forward slashes.
9
10
Directory ( Utf8PathBuf ) ,
10
11
#[ cfg( feature = "include-dir" ) ]
11
12
IncludeDir ( std:: borrow:: Cow < ' static , include_dir:: Dir < ' static > > ) ,
@@ -31,18 +32,23 @@ impl DataSource {
31
32
///
32
33
/// Used for `--exact` matches.
33
34
pub ( crate ) fn derive_exact ( & self , filter : & str , test_name : & str ) -> Option < TestEntry > {
34
- let rel_path = filter. strip_prefix ( test_name) ?. strip_prefix ( "::" ) ?;
35
+ // include_dir 0.7.4 returns paths with forward slashes, including on
36
+ // Windows. But that isn't part of the stable API it seems, so we call
37
+ // `rel_path_to_forward_slashes` anyway.
38
+ let rel_path = rel_path_to_forward_slashes (
39
+ filter. strip_prefix ( test_name) ?. strip_prefix ( "::" ) ?. as_ref ( ) ,
40
+ ) ;
35
41
match self {
36
42
DataSource :: Directory ( path) => Some ( TestEntry {
37
- source : TestSource :: Path ( path. join ( rel_path) ) ,
38
- rel_path : rel_path . into ( ) ,
43
+ source : TestSource :: Path ( rel_path_to_forward_slashes ( & path. join ( & rel_path) ) ) ,
44
+ rel_path,
39
45
} ) ,
40
46
#[ cfg( feature = "include-dir" ) ]
41
47
DataSource :: IncludeDir ( dir) => {
42
- let file = dir. get_file ( rel_path) ?;
48
+ let file = dir. get_file ( & rel_path) ?;
43
49
Some ( TestEntry {
44
50
source : TestSource :: IncludeDir ( file) ,
45
- rel_path : rel_path . into ( ) ,
51
+ rel_path,
46
52
} )
47
53
}
48
54
}
@@ -122,8 +128,15 @@ fn iter_include_dir<'a>(
122
128
stack : dir. entries ( ) . iter ( ) . collect ( ) ,
123
129
}
124
130
. map ( |file| {
125
- let rel_path = Utf8PathBuf :: try_from ( file. path ( ) . to_path_buf ( ) )
126
- . map_err ( |error| error. into_io_error ( ) ) ?;
131
+ // include_dir 0.7.4 returns paths with forward slashes, including on
132
+ // Windows. But that isn't part of the stable API it seems, so we call
133
+ // `rel_path_to_forward_slashes` anyway.
134
+ let rel_path = match file. path ( ) . try_into ( ) {
135
+ Ok ( path) => rel_path_to_forward_slashes ( path) ,
136
+ Err ( error) => {
137
+ return Err ( error. into_io_error ( ) ) ;
138
+ }
139
+ } ;
127
140
Ok ( TestEntry {
128
141
source : TestSource :: IncludeDir ( file) ,
129
142
rel_path,
@@ -139,10 +152,11 @@ pub(crate) struct TestEntry {
139
152
140
153
impl TestEntry {
141
154
pub ( crate ) fn from_full_path ( root : & Utf8Path , path : Utf8PathBuf ) -> Self {
142
- let rel_path = path
143
- . strip_prefix ( root)
144
- . unwrap_or_else ( |_| panic ! ( "failed to strip root '{}' from path '{}'" , root, path) )
145
- . to_owned ( ) ;
155
+ let path = rel_path_to_forward_slashes ( & path) ;
156
+ let rel_path =
157
+ rel_path_to_forward_slashes ( path. strip_prefix ( root) . unwrap_or_else ( |_| {
158
+ panic ! ( "failed to strip root '{}' from path '{}'" , root, path)
159
+ } ) ) ;
146
160
Self {
147
161
source : TestSource :: Path ( path) ,
148
162
rel_path,
@@ -180,10 +194,18 @@ impl TestEntry {
180
194
}
181
195
}
182
196
183
- /// Returns the path to the test data.
197
+ /// Returns the path to match regexes against.
198
+ ///
199
+ /// This is always the relative path to the file from the include directory.
200
+ pub ( crate ) fn match_path ( & self ) -> & Utf8Path {
201
+ & self . rel_path
202
+ }
203
+
204
+ /// Returns the path to the test data, as passed into the test function.
184
205
///
185
- /// For directories on disk, this is the absolute path. For `include_dir`
186
- /// sources, this is the path relative to the root of the include directory.
206
+ /// For directories on disk, this is the relative path after being joined
207
+ /// with the include directory. For `include_dir` sources, this is the path
208
+ /// relative to the root of the include directory.
187
209
pub ( crate ) fn test_path ( & self ) -> & Utf8Path {
188
210
match & self . source {
189
211
TestSource :: Path ( path) => path,
@@ -219,9 +241,37 @@ impl TestEntry {
219
241
}
220
242
}
221
243
244
+ #[ cfg( windows) ]
245
+ #[ track_caller]
246
+ fn rel_path_to_forward_slashes ( path : & Utf8Path ) -> Utf8PathBuf {
247
+ assert ! ( is_truly_relative( path) , "path {path} must be relative" ) ;
248
+ path. as_str ( ) . replace ( '\\' , "/" ) . into ( )
249
+ }
250
+
251
+ #[ cfg( not( windows) ) ]
252
+ #[ track_caller]
253
+ fn rel_path_to_forward_slashes ( path : & Utf8Path ) -> Utf8PathBuf {
254
+ assert ! ( is_truly_relative( path) , "path {path} must be relative" ) ;
255
+ path. to_owned ( )
256
+ }
257
+
258
+ /// Returns true if this is a path with no root-dir or prefix components.
259
+ ///
260
+ /// On Windows, unlike `path.is_relative()`, this rejects paths like "C:temp"
261
+ /// and "\temp".
262
+ #[ track_caller]
263
+ fn is_truly_relative ( path : & Utf8Path ) -> bool {
264
+ path. components ( ) . all ( |c| match c {
265
+ Utf8Component :: Normal ( _) | Utf8Component :: CurDir | Utf8Component :: ParentDir => true ,
266
+ Utf8Component :: RootDir | Utf8Component :: Prefix ( _) => false ,
267
+ } )
268
+ }
269
+
222
270
#[ derive( Debug ) ]
223
271
#[ doc( hidden) ]
224
272
pub ( crate ) enum TestSource {
273
+ /// A data source on disk, with the path being the relative path to the file
274
+ /// from the crate root.
225
275
Path ( Utf8PathBuf ) ,
226
276
#[ cfg( feature = "include-dir" ) ]
227
277
IncludeDir ( & ' static include_dir:: File < ' static > ) ,
@@ -256,7 +306,14 @@ pub mod data_source_kinds {
256
306
257
307
impl < T : ToString > AsDirectory for T {
258
308
fn resolve_data_source ( self ) -> DataSource {
259
- DataSource :: Directory ( self . to_string ( ) . into ( ) )
309
+ let s = self . to_string ( ) ;
310
+ let path = Utf8Path :: new ( & s) ;
311
+
312
+ if !is_truly_relative ( path) {
313
+ panic ! ( "data source path must be relative: '{}'" , s) ;
314
+ }
315
+
316
+ DataSource :: Directory ( rel_path_to_forward_slashes ( path) )
260
317
}
261
318
}
262
319
@@ -290,6 +347,12 @@ pub mod data_source_kinds {
290
347
mod tests {
291
348
use super :: * ;
292
349
350
+ #[ test]
351
+ #[ should_panic = "data source path must be relative: '/absolute/path'" ]
352
+ fn data_source_absolute_path_panics ( ) {
353
+ data_source_kinds:: AsDirectory :: resolve_data_source ( "/absolute/path" ) ;
354
+ }
355
+
293
356
#[ test]
294
357
fn missing_test_name ( ) {
295
358
assert_eq ! ( derive_test_path( "root" . into( ) , "file" , "test_name" ) , None ) ;
0 commit comments