Skip to content

Commit ad12f63

Browse files
committed
re-allow absolute paths
Fixes #252.
1 parent 81cac0b commit ad12f63

File tree

5 files changed

+91
-40
lines changed

5 files changed

+91
-40
lines changed

README.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,8 @@ datatest_stable::harness! {
5757
extra `Vec<u8>` parameter is specified, the contents of the file will be loaded and passed
5858
in as a `Vec<u8>` (erroring out if that failed).
5959
* `root` - The path to the root directory where the input files (fixtures)
60-
live. This path is relative to the root of the crate (the directory where
61-
the crate’s `Cargo.toml` is located). Absolute paths are not supported, and
62-
will panic at runtime.
60+
live. Relative paths are resolved relative to the crate root (the directory where the crate’s
61+
`Cargo.toml` is located).
6362

6463
`root` is an arbitrary expression that implements
6564
[`Display`](https://doc.rust-lang.org/nightly/core/fmt/trait.Display.html), such as `&str`, or a function call that
@@ -80,17 +79,20 @@ datatest_stable::harness! {
8079
}
8180
````
8281

83-
### Relative paths
82+
### Relative and absolute paths
8483

8584
The `pattern` argument is tested against the **relative** path of each file,
8685
**excluding** the `root` prefix – not the absolute path.
8786

88-
The `Path` and `Utf8Path` passed into the test functions are **relative** to
89-
the root of the crate, obtained by joining `root` to the relative path of
90-
the file.
87+
The `Path` and `Utf8Path` passed into the test functions are created by
88+
joining `root` to the relative path of the file.
9189

92-
For universality, all relative paths use `/` as the path separator,
93-
including on Windows.
90+
* If `root` is **relative**, the paths passed in are relative to the crate root.
91+
* If `root` is **absolute**, the paths passed in are absolute.
92+
93+
For uniformity, all relative paths use `/` as the path separator,
94+
including on Windows, and all absolute paths use the platform’s native
95+
separator throughout.
9496

9597
### Examples
9698

@@ -170,9 +172,9 @@ datatest_stable::harness! {
170172
}
171173
````
172174

173-
In this case, the passed-in `Path` and `Utf8Path` are **relative** to the
174-
root of the included directory. Like elsewhere in `datatest-stable`, these
175-
relative paths always use forward slashes as separators, including on
175+
In this case, the passed-in `Path` and `Utf8Path` are always **relative** to
176+
the root of the included directory. Like elsewhere in `datatest-stable`,
177+
these relative paths always use forward slashes as separators, including on
176178
Windows.
177179

178180
Because the files don’t exist on disk, the test functions must accept their

src/data_source.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use camino::{Utf8Component, Utf8Path, Utf8PathBuf};
66
#[derive(Debug)]
77
#[doc(hidden)]
88
pub enum DataSource {
9-
// This is relative to the crate root, and stored with forward slashes.
9+
// The path has had normalize_slashes applied to it.
1010
Directory(Utf8PathBuf),
1111
#[cfg(feature = "include-dir")]
1212
IncludeDir(std::borrow::Cow<'static, include_dir::Dir<'static>>),
@@ -40,7 +40,7 @@ impl DataSource {
4040
);
4141
match self {
4242
DataSource::Directory(path) => Some(TestEntry {
43-
source: TestSource::Path(rel_path_to_forward_slashes(&path.join(&rel_path))),
43+
source: TestSource::Path(normalize_slashes(&path.join(&rel_path))),
4444
rel_path,
4545
}),
4646
#[cfg(feature = "include-dir")]
@@ -152,7 +152,7 @@ pub(crate) struct TestEntry {
152152

153153
impl TestEntry {
154154
pub(crate) fn from_full_path(root: &Utf8Path, path: Utf8PathBuf) -> Self {
155-
let path = rel_path_to_forward_slashes(&path);
155+
let path = normalize_slashes(&path);
156156
let rel_path =
157157
rel_path_to_forward_slashes(path.strip_prefix(root).unwrap_or_else(|_| {
158158
panic!("failed to strip root '{}' from path '{}'", root, path)
@@ -241,13 +241,29 @@ impl TestEntry {
241241
}
242242
}
243243

244+
#[cfg(windows)]
245+
#[track_caller]
246+
fn normalize_slashes(path: &Utf8Path) -> Utf8PathBuf {
247+
if is_truly_relative(path) {
248+
rel_path_to_forward_slashes(path)
249+
} else {
250+
path.as_str().replace('/', "\\").into()
251+
}
252+
}
253+
244254
#[cfg(windows)]
245255
#[track_caller]
246256
fn rel_path_to_forward_slashes(path: &Utf8Path) -> Utf8PathBuf {
247257
assert!(is_truly_relative(path), "path {path} must be relative");
248258
path.as_str().replace('\\', "/").into()
249259
}
250260

261+
#[cfg(not(windows))]
262+
#[track_caller]
263+
fn normalize_slashes(path: &Utf8Path) -> Utf8PathBuf {
264+
path.to_owned()
265+
}
266+
251267
#[cfg(not(windows))]
252268
#[track_caller]
253269
fn rel_path_to_forward_slashes(path: &Utf8Path) -> Utf8PathBuf {
@@ -309,11 +325,7 @@ pub mod data_source_kinds {
309325
let s = self.to_string();
310326
let path = Utf8Path::new(&s);
311327

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))
328+
DataSource::Directory(normalize_slashes(path))
317329
}
318330
}
319331

@@ -347,12 +359,6 @@ pub mod data_source_kinds {
347359
mod tests {
348360
use super::*;
349361

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-
356362
#[test]
357363
fn missing_test_name() {
358364
assert_eq!(derive_test_path("root".into(), "file", "test_name"), None);

src/lib.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,8 @@
5151
//! in as a `Vec<u8>` (erroring out if that failed).
5252
//!
5353
//! * `root` - The path to the root directory where the input files (fixtures)
54-
//! live. This path is relative to the root of the crate (the directory where
55-
//! the crate's `Cargo.toml` is located). Absolute paths are not supported, and
56-
//! will panic at runtime.
54+
//! live. Relative paths are resolved relative to the crate root (the directory where the crate's
55+
//! `Cargo.toml` is located).
5756
//!
5857
//! `root` is an arbitrary expression that implements
5958
//! [`Display`](std::fmt::Display), such as `&str`, or a function call that
@@ -74,17 +73,20 @@
7473
//! }
7574
//! ```
7675
//!
77-
//! ## Relative paths
76+
//! ## Relative and absolute paths
7877
//!
7978
//! The `pattern` argument is tested against the **relative** path of each file,
8079
//! **excluding** the `root` prefix -- not the absolute path.
8180
//!
82-
//! The `Path` and `Utf8Path` passed into the test functions are **relative** to
83-
//! the root of the crate, obtained by joining `root` to the relative path of
84-
//! the file.
81+
//! The `Path` and `Utf8Path` passed into the test functions are created by
82+
//! joining `root` to the relative path of the file.
8583
//!
86-
//! For universality, all relative paths use `/` as the path separator,
87-
//! including on Windows.
84+
//! * If `root` is **relative**, the paths passed in are relative to the crate root.
85+
//! * If `root` is **absolute**, the paths passed in are absolute.
86+
//!
87+
//! For uniformity, all relative paths use `/` as the path separator,
88+
//! including on Windows, and all absolute paths use the platform's native
89+
//! separator throughout.
8890
//!
8991
//! ## Examples
9092
//!
@@ -166,9 +168,9 @@
166168
//! }
167169
//! ```
168170
//!
169-
//! In this case, the passed-in `Path` and `Utf8Path` are **relative** to the
170-
//! root of the included directory. Like elsewhere in `datatest-stable`, these
171-
//! relative paths always use forward slashes as separators, including on
171+
//! In this case, the passed-in `Path` and `Utf8Path` are always **relative** to
172+
//! the root of the included directory. Like elsewhere in `datatest-stable`,
173+
//! these relative paths always use forward slashes as separators, including on
172174
//! Windows.
173175
//!
174176
//! Because the files don't exist on disk, the test functions must accept their

tests/example.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,23 @@ fn test_artifact_utf8(path: &Utf8Path) -> Result<()> {
1717
test_artifact(path.as_ref())
1818
}
1919

20+
fn test_artifact_utf8_abs(path: &Utf8Path) -> Result<()> {
21+
// path must be absolute
22+
assert!(path.is_absolute(), "path must be absolute: {:?}", path);
23+
24+
// On Windows, the path must be normalized to use backslashes.
25+
#[cfg(windows)]
26+
{
27+
assert!(
28+
!path.as_str().contains('/'),
29+
"path must not contain forward slashes: {:?}",
30+
path
31+
);
32+
}
33+
34+
test_artifact(path.as_ref())
35+
}
36+
2037
#[cfg(feature = "include-dir")]
2138
#[macro_use]
2239
mod with_contents {
@@ -132,6 +149,14 @@ static TESTS_FILES_MAIN_SEP: &str = "tests\\files";
132149
#[cfg(not(windows))]
133150
static TESTS_FILES_MAIN_SEP: &str = "tests/files";
134151

152+
fn tests_files_abs() -> String {
153+
std::env::current_dir()
154+
.expect("current dir obtained")
155+
.join("tests/files")
156+
.to_string_lossy()
157+
.into_owned()
158+
}
159+
135160
datatest_stable::harness! {
136161
{
137162
test = test_artifact,
@@ -146,6 +171,16 @@ datatest_stable::harness! {
146171
// This regex pattern matches all files.
147172
pattern = r"^.*\.txt$",
148173
},
174+
{
175+
test = test_artifact_utf8_abs,
176+
// Ensure that tests/files in an absolute path is normalized to
177+
// tests\files on Windows.
178+
root = tests_files_abs(),
179+
// This regex matches exactly dir/a.txt, b.txt, and c.skip.txt -- this
180+
// ensures that patterns are relative to the include dir and not the
181+
// crate root.
182+
pattern = r"^(dir/a|b|c\.skip)\.txt$",
183+
},
149184
{
150185
test = with_contents::test_artifact_string,
151186
root = maybe_include_dir!(),

tests/integration.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ static EXPECTED_LINES: &[&str] = &[
55
"datatest-stable::example test_artifact_utf8::dir/a.txt",
66
"datatest-stable::example test_artifact_utf8::b.txt",
77
"datatest-stable::example test_artifact_utf8::c.skip.txt",
8+
"datatest-stable::example test_artifact_utf8_abs::dir/a.txt",
9+
"datatest-stable::example test_artifact_utf8_abs::b.txt",
10+
"datatest-stable::example test_artifact_utf8_abs::c.skip.txt",
811
"datatest-stable::example test_artifact::dir/a.txt",
912
"datatest-stable::example test_artifact::b.txt",
1013
"datatest-stable::example with_contents::test_artifact_bytes::dir/a.txt",
@@ -43,7 +46,7 @@ fn run_example() {
4346
for line in EXPECTED_LINES
4447
.iter()
4548
.copied()
46-
.chain(std::iter::once("17 tests run: 17 passed, 0 skipped"))
49+
.chain(std::iter::once("20 tests run: 20 passed, 0 skipped"))
4750
{
4851
assert!(
4952
stderr.contains(line),
@@ -69,6 +72,9 @@ mod unix {
6972
"datatest-stable::example test_artifact_utf8::dir/a.txt",
7073
"datatest-stable::example test_artifact_utf8::b.txt",
7174
"datatest-stable::example test_artifact_utf8::c.skip.txt",
75+
"datatest-stable::example test_artifact_utf8_abs::dir/a.txt",
76+
"datatest-stable::example test_artifact_utf8_abs::b.txt",
77+
"datatest-stable::example test_artifact_utf8_abs::c.skip.txt",
7278
"datatest-stable::example test_artifact::::colon::dir/::.txt",
7379
"datatest-stable::example test_artifact::::colon::dir/a.txt",
7480
"datatest-stable::example test_artifact::dir/a.txt",
@@ -135,7 +141,7 @@ mod unix {
135141
.iter()
136142
.chain(EXPECTED_UNIX_LINES.iter())
137143
.copied()
138-
.chain(std::iter::once("27 tests run: 27 passed, 0 skipped"))
144+
.chain(std::iter::once("30 tests run: 30 passed, 0 skipped"))
139145
{
140146
assert!(
141147
stderr.contains(line),

0 commit comments

Comments
 (0)