use env_logger;
use tempfile;
use super::*;
use std::env;
use std::fs;
#[test]
fn relative_accepts_empty_path() {
let _ = env_logger::try_init();
let actual = Relative::new("").expect("Could not parse empty path");
let expected = path::Path::new("");
assert_eq!(actual.0, expected);
}
fn create_cookie_at<P: AsRef<path::Path>>(path: P) -> String {
use std::io::Write;
let res = format!(
"pid: {} time: {:?}",
std::process::id(),
std::time::Instant::now(),
);
println!("Creating cookie at {:?}", path.as_ref());
let mut handle = fs::File::create(path).expect("Could not create file?");
handle
.write_all(res.as_bytes())
.expect("Could not write message?");
res
}
fn check_cookie_at<P: AsRef<path::Path>>(expected: &str, path: P) {
let actual = {
use std::io::Read;
println!("Checking cookie at {:?}", path.as_ref());
let mut handle = fs::File::open(path).expect("Could not open file?");
let mut res = String::new();
handle
.read_to_string(&mut res)
.expect("Could not read message?");
res
};
assert_eq!(actual, expected);
}
#[cfg(windows)]
fn symlink_dir<P, Q>(src: P, dst: Q)
where
P: AsRef<path::Path>,
Q: AsRef<path::Path>,
{
use std::os::windows::fs;
let dst = dst.as_ref();
fs::symlink_dir(src, &dst).expect(
"Could not create symbolic link? \
Run as Administrator, or on Windows 10 in Developer Mode",
);
dst.symlink_metadata().expect(
"Link creation succeeded, but can't read link? \
If you're using Wine, see bug 44948",
);
}
#[cfg(unix)]
fn symlink_dir<P, Q>(src: P, dst: Q)
where
P: AsRef<path::Path>,
Q: AsRef<path::Path>,
{
use std::os::unix::fs;
fs::symlink(src, dst).expect("Could not create symbolic link?");
}
#[test]
fn simple_absolute_path_works_after_cleaning() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
let normal_path = temp.path().join("foo.txt");
let cookie = create_cookie_at(&normal_path);
check_cookie_at(
&cookie,
Absolute::new(normal_path).expect("Could not clean absolute path?"),
);
}
#[test]
fn absolute_path_with_single_dot_works_after_cleaning() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
let normal_path = temp.path().join("./././foo.txt");
let cookie = create_cookie_at(&normal_path);
check_cookie_at(
&cookie,
Absolute::new(normal_path)
.expect("Could not clean path with single dots?"),
);
}
#[test]
fn absolute_path_with_double_dot_from_existing_dir_works_after_cleaning() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
let parent = temp.path().join("parent");
fs::create_dir_all(&parent).expect("Could not create parent dir?");
let normal_path = parent.join("../foo.txt");
let cookie = create_cookie_at(&normal_path);
check_cookie_at(
&cookie,
Absolute::new(normal_path)
.expect("Could not clean path with double dots?"),
);
}
#[test]
fn double_dot_after_relative_symlink_works_as_expected() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
fs::create_dir_all(temp.path().join("apple/ant"))
.expect("Could not create apple/ant dir?");
fs::create_dir_all(temp.path().join("banana"))
.expect("Could not create banana dir?");
symlink_dir("../apple/ant", temp.path().join("banana/bat"));
let normal_path = temp.path().join("banana/bat/../foo.txt");
let cookie = create_cookie_at(&normal_path);
check_cookie_at(
&cookie,
Absolute::new(normal_path)
.expect("Could not clean path through a symlink?"),
);
}
#[test]
fn double_dot_after_absolute_symlink_works_as_expected() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
fs::create_dir_all(temp.path().join("apple/ant"))
.expect("Could not create apple/ant dir?");
fs::create_dir_all(temp.path().join("banana"))
.expect("Could not create banana dir?");
symlink_dir(
temp.path().join("apple/ant"),
temp.path().join("banana/bat"),
);
let normal_path = temp.path().join("banana/bat/../foo.txt");
let cookie = create_cookie_at(&normal_path);
check_cookie_at(
&cookie,
Absolute::new(normal_path)
.expect("Could not clean path through a symlink?"),
);
}
#[test]
fn cleaning_is_idempotent() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
let first =
Absolute::new(temp.path()).expect("Could not clean sensible path?");
let second =
Absolute::new(&first).expect("Could not re-clean sensible path?");
assert_eq!(first, second);
}
#[cfg(windows)]
mod windows {
use super::*;
#[test]
fn absolute_path_with_double_dot_from_missing_dir_works_after_cleaning() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
let normal_path = temp.path().join("foo/../bar.txt");
let cookie = create_cookie_at(&normal_path);
check_cookie_at(
&cookie,
Absolute::new(normal_path)
.expect("Could not clean path with double dots?"),
);
}
#[test]
fn absolute_path_gets_canonical_prefix() {
let _ = env_logger::try_init();
let path = Absolute::new("C:\\foo\\bar")
.expect("Could not handle an absolute path");
assert_eq!(path.0, path::Path::new("\\\\?\\C:\\foo\\bar"));
}
#[test]
fn missing_absolute_path_with_double_dot_is_normalized() {
let _ = env_logger::try_init();
let path = Absolute::new("C:\\foo\\..\\bar")
.expect("Could not handle an absolute path");
assert_eq!(path.0, path::Path::new("\\\\?\\C:\\bar"));
}
#[test]
fn absolute_path_with_single_dot_is_dropped() {
let _ = env_logger::try_init();
let path = Absolute::new("C:\\foo\\.\\bar")
.expect("Could not handle an absolute path");
assert_eq!(path.0, path::Path::new("\\\\?\\C:\\foo\\bar"));
}
#[test]
fn absolute_path_with_slashes_is_normalized() {
let _ = env_logger::try_init();
let path = Absolute::new("C:/foo/bar")
.expect("Could not handle an absolute path");
assert_eq!(path.0, path::Path::new("\\\\?\\C:\\foo\\bar"));
}
#[test]
fn relative_path_is_relative_to_current_directory() {
let _ = env_logger::try_init();
let path =
Absolute::new("foo").expect("Could not handle a relative path");
let current_dir = env::current_dir()
.expect("Could not read current directory.")
.canonicalize()
.expect("Could not canonicalize current directory.");
let expected = current_dir.join("foo");
assert_eq!(path.0, expected);
}
#[test]
fn leading_double_dot_in_relative_path() {
let _ = env_logger::try_init();
let path =
Absolute::new("..\\foo").expect("Could not handle a relative path");
let current_dir = env::current_dir()
.expect("Could not read current directory.")
.canonicalize()
.expect("Could not canonicalize current directory.");
let expected = current_dir
.parent()
.unwrap_or(path::Path::new("/"))
.join("foo");
assert_eq!(path.0, expected);
}
#[test]
fn skip_leading_dot_in_relative_path() {
let _ = env_logger::try_init();
let path =
Absolute::new(".\\foo").expect("Could not handle a relative path");
let current_dir = env::current_dir()
.expect("Could not read current directory.")
.canonicalize()
.expect("Could not canonicalize current directory.");
let expected = current_dir.join("foo");
assert_eq!(path.0, expected);
}
#[test]
fn prefix_relative_path_is_relative_to_current_drive() {
let _ = env_logger::try_init();
let actual = Absolute::new("\\foo")
.expect("Could not handle a prefix-relative path");
let expected = env::current_dir()
.expect("Could not read current directory.")
.canonicalize()
.expect("Could not canonicalize current directory?")
.join("\\foo");
assert_eq!(actual.0, expected);
}
#[test]
fn drive_relative_path_uses_drive_current_path() {
let _ = env_logger::try_init();
let current_drive = env::current_dir()
.expect("Could not read current directory")
.components()
.next()
.expect("Current directory is an empty path?")
.as_os_str()
.to_os_string();
let mut input = current_drive.clone();
input.push("foo");
let actual = Absolute::new(input)
.expect("Could not handle a drive-relative path");
let expected = env::current_dir()
.expect("Could not read current directory.")
.canonicalize()
.expect("Could not canonicalize current directory.")
.join("foo");
assert_eq!(actual.0, expected);
}
#[test]
fn do_not_resolve_every_symlink() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
let target_path = temp.path().join("target");
let link_path = temp.path().join("link");
println!("Creating target path: {:?}", target_path);
fs::OpenOptions::new()
.create(true)
.write(true)
.open(&target_path)
.expect("Could not create target");
assert_eq!(target_path.exists(), true);
println!("Creating link path: {:?}", link_path);
symlink_dir(&target_path, &link_path);
println!("Absolutizing symlink path");
let actual =
Absolute::new(&link_path).expect("Could not handle symlink path");
println!("Actual: {:?}", actual.0);
let expected = temp
.path()
.canonicalize()
.expect("Could not canonicalize symlink path")
.join("link");
assert_eq!(actual.0, expected);
}
#[test]
fn cannot_go_up_from_root_directory() {
let _ = env_logger::try_init();
let actual = Absolute::new("C:\\..\\foo")
.expect("Could not handle going up from the root directory");
let expected = fs::canonicalize("C:\\")
.expect("No C:\\ directory?")
.join("foo");
assert_eq!(actual.0, expected);
}
#[test]
fn do_not_resolve_absolute_symlinks() {
let _ = env_logger::try_init();
let temp = tempfile::tempdir()
.expect("Could not make temporary directory.")
.into_path();
let link_path = temp.join("link");
debug!("Creating link at: {:?}", link_path);
symlink_dir("C:\\does\\not\\exist", &link_path);
let actual = Absolute::new(link_path.join("..\\bar"))
.expect("Could not handle symlink with absolute target");
let expected = link_path
.parent()
.expect("Temporary directory is rooot?")
.canonicalize()
.expect("Could not canonicalize?")
.join("bar");
assert_eq!(actual.0, expected);
}
#[test]
fn absolute_accepts_dereferencing_cyclic_symlinks() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
symlink_dir("a", temp.path().join("b"));
symlink_dir("b", temp.path().join("a"));
let actual = Absolute::new(temp.path().join("a/../tail"))
.expect("Could not handle cyclic symlinks?");
let expected = temp
.path()
.canonicalize()
.expect("Could not canonicalize temp path?")
.join("tail");
assert_eq!(actual.0, expected);
}
#[test]
fn absolute_accepts_navigating_cyclic_symlinks() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
symlink_dir(".", temp.path().join("cur"));
let actual = Absolute::new(temp.path().join("cur/cur/cur/cur/cur/a"))
.expect("Could not handle cyclic path.");
let expected = temp
.path()
.canonicalize()
.expect("Could not canonicalize temp dir")
.join("cur\\cur\\cur\\cur\\cur\\a");
assert_eq!(actual.0, expected);
}
#[test]
fn absolute_rejects_invalid_prefix() {
let _ = env_logger::try_init();
let err = Absolute::new("\\\\?**?\\bogus\\path\\")
.map(|p| {
panic!("Parsing bogus path should have failed! {:?}", p);
})
.err()
.unwrap();
match err {
Error::IoError { err, at } => {
assert_eq!(err.kind(), io::ErrorKind::Other);
assert_eq!(at, path::Path::new("\\\\?**?\\bogus"));
}
e => panic!("Got unexpected error {:?}", e),
}
}
#[test]
fn relative_accepts_monotonic_path() {
let _ = env_logger::try_init();
let actual = Relative::new("does\\not\\exist")
.expect("Could not parse monotonic path");
let expected = path::Path::new("does\\not\\exist");
assert_eq!(actual.0, expected);
}
#[test]
fn relative_normalises_slashes() {
let _ = env_logger::try_init();
let actual = Relative::new("does/not/exist")
.expect("Could not parse path with slashes");
let expected = path::Path::new("does\\not\\exist");
assert_eq!(actual.0, expected);
}
#[test]
fn relative_normalises_single_dots() {
let _ = env_logger::try_init();
let actual = Relative::new("does\\.\\not\\.\\exist")
.expect("Could not parse path with CurrentDir components");
let expected = path::Path::new("does\\not\\exist");
assert_eq!(actual.0, expected);
}
#[test]
fn relative_normalises_inner_double_dots() {
let _ = env_logger::try_init();
let actual = Relative::new("does\\..\\not\\exist")
.expect("Could not parse path with ParentDir component");
let expected = path::Path::new("not\\exist");
assert_eq!(actual.0, expected);
}
#[test]
fn relative_rejects_leading_double_dots() {
let _ = env_logger::try_init();
let err = Relative::new("..\\does\\not\\exist")
.err()
.expect("Parsing leading '..' should have failed!");
match err {
Error::RelativePathEscapesPrefix(p) => {
assert_eq!(p, path::Path::new("..\\does\\not\\exist"))
}
e => panic!("Got unexpected error {:?}", e),
}
}
#[test]
fn relative_rejects_fully_absolute_path() {
let _ = env_logger::try_init();
let err = Relative::new("C:\\absolute\\path")
.err()
.expect("Parsing an absolute path should have failed!");
match err {
Error::PathIsAbsolute(p) => {
assert_eq!(p, path::Path::new("C:\\absolute\\path"))
}
e => panic!("Got unexpected error {:?}", e),
}
}
#[test]
fn relative_rejects_prefix_relative_path() {
let _ = env_logger::try_init();
let err = Relative::new("\\relative\\path")
.err()
.expect("Parsing prefix-relative path should have failed!");
match err {
Error::PathIsAbsolute(p) => {
assert_eq!(p, path::Path::new("\\relative\\path"))
}
e => panic!("Got unexpected error {:?}", e),
}
}
#[test]
fn relative_rejects_drive_relative_path() {
let _ = env_logger::try_init();
let err = Relative::new("C:relative\\path")
.err()
.expect("Parsing drive-relative path should have failed!");
match err {
Error::PathIsAbsolute(p) => {
assert_eq!(p, path::Path::new("C:relative\\path"))
}
e => panic!("Got unexpected error {:?}", e),
}
}
#[test]
fn relative_rejects_relative_path_escaping_prefix() {
let _ = env_logger::try_init();
let err = Relative::new("inside\\..\\..\\outside")
.err()
.expect("Parsing escaping path should have failed!");
match err {
Error::RelativePathEscapesPrefix(p) => {
assert_eq!(p, path::Path::new("inside\\..\\..\\outside"))
}
e => panic!("Got unexpected error {:?}", e),
}
}
}
#[cfg(unix)]
mod unix {
use super::*;
use std::os::unix::fs as unixfs;
#[test]
fn missing_absolute_path_is_unchanged() {
let _ = env_logger::try_init();
let expected = path::Path::new("/some/absolute/path");
let path = Absolute::new(&expected)
.expect("Could not handle an absolute path");
assert_eq!(path.0, expected);
}
#[test]
fn missing_absolute_path_with_double_dot_is_normalized() {
let _ = env_logger::try_init();
let actual = Absolute::new("/some/absolute/../path")
.expect("Could not handle an absolute path");
let expected = path::Path::new("/some/path");
assert_eq!(actual.0, expected);
}
#[test]
fn existing_path_with_double_dot_is_normalized() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
fs::create_dir_all(temp.path().join("apple"))
.expect("Could not create apple directory.");
fs::create_dir_all(temp.path().join("banana"))
.expect("Could not create apple directory.");
let actual = Absolute::new(temp.path().join("banana/../apple"))
.expect("Could not resolve existing directories.");
let expected = temp.path().join("apple");
assert_eq!(actual.0, expected);
}
#[test]
fn missing_relative_path_is_relative_to_current_directory() {
let _ = env_logger::try_init();
let actual = Absolute::new("does/not/exist")
.expect("Could not handle a missing relative path");
let expected = env::current_dir()
.expect("Could not get current directory")
.join("does/not/exist");
assert_eq!(actual.0, expected);
}
#[test]
fn skip_leading_dots() {
let _ = env_logger::try_init();
let actual = Absolute::new("/some/./missing/./path")
.expect("Could not handle a path with dot components");
let expected = path::Path::new("/some/missing/path");
assert_eq!(actual.0, expected);
}
#[test]
fn leading_double_dot_in_relative_path() {
let _ = env_logger::try_init();
let actual = Absolute::new("../does/not/exist")
.expect("Could not handle a relative path");
let expected = env::current_dir()
.expect("Could not get current directory")
.parent()
.unwrap_or_else(|| path::Path::new("/"))
.join("does/not/exist");
assert_eq!(actual.0, expected);
}
#[test]
fn do_not_resolve_every_symlink() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
let target_path = temp.path().join("target");
let link_path = temp.path().join("link");
fs::OpenOptions::new()
.create(true)
.write(true)
.open(&target_path)
.expect("Could not create target");
unixfs::symlink(&target_path, &link_path)
.expect("Could not create symbolic link.");
let actual =
Absolute::new(&link_path).expect("Could not handle symlink path");
assert_eq!(actual.0, link_path);
}
#[test]
fn cannot_go_up_from_root_directory() {
let _ = env_logger::try_init();
let actual = Absolute::new("/../foo")
.expect("Could not handle going up from the root directory");
let expected = path::Path::new("/foo");
assert_eq!(actual.0, expected);
}
#[test]
fn resolve_absolute_symlinks() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
unixfs::symlink("/does/not/exist", temp.path().join("link"))
.expect("Could not create symlink");
let actual = Absolute::new(temp.path().join("link/../bar"))
.expect("Could not handle symlink with absolute target");
let expected = path::Path::new("/does/not/bar");
assert_eq!(actual.0, expected);
}
#[test]
fn absolute_rejects_dereferencing_cyclic_symlinks() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
unixfs::symlink("a", temp.path().join("b"))
.expect("Could not create symlink b->a");
unixfs::symlink("b", temp.path().join("a"))
.expect("Could not create symlink a->b");
let err = Absolute::new(temp.path().join("a/../tail"))
.expect_err("Cyclic symlinks dereferenced?");
match err {
Error::SymlinkLoops(_) => (),
e => panic!("Got unexpected error: {:?}", e),
}
}
#[test]
fn absolute_accepts_navigating_cyclic_symlinks() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
unixfs::symlink("a", temp.path().join("b"))
.expect("Could not create symlink b->a");
unixfs::symlink("b", temp.path().join("a"))
.expect("Could not create symlink a->b");
let expected = temp.path().join("a/tail");
let actual =
Absolute::new(&expected).expect("Could not handle cyclic path.");
assert_eq!(actual.0, expected);
}
#[test]
fn absolute_reports_permission_error() {
let _ = env_logger::try_init();
let temp =
tempfile::tempdir().expect("Could not make temporary directory.");
fs::create_dir_all(temp.path().join("dir/secret"))
.expect("Could not create directory");
use self::unixfs::PermissionsExt;
fs::set_permissions(
temp.path().join("dir"),
fs::Permissions::from_mode(0),
)
.expect("Could not set permissions");
let err = Absolute::new(temp.path().join("dir/secret/.."))
.map(|p| {
panic!("Getting absolute path should have failed: {:?}", p);
})
.err()
.unwrap();
match err {
Error::IoError { err, at } => {
assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
assert_eq!(at, temp.path().join("dir/secret"));
}
e => panic!("Got unexpected error: {:?}", e),
}
}
#[test]
fn relative_accepts_monotonic_path() {
let actual = Relative::new("does/not/exist")
.expect("Could not parse monotonic path");
let expected = path::Path::new("does/not/exist");
assert_eq!(actual.0, expected);
}
#[test]
fn relative_normalises_single_dots() {
let actual = Relative::new("does/./not/./exist")
.expect("Could not parse path with CurrentDir components");
let expected = path::Path::new("does/not/exist");
assert_eq!(actual.0, expected);
}
#[test]
fn relative_normalises_inner_double_dots() {
let actual = Relative::new("does/../not/exist")
.expect("Could not parse path with ParentDir component");
let expected = path::Path::new("not/exist");
assert_eq!(actual.0, expected);
}
#[test]
fn relative_rejects_leading_double_dots() {
let err = Relative::new("../does/not/exist")
.err()
.expect("Parsing leading '..' should have failed!");
match err {
Error::RelativePathEscapesPrefix(p) => {
assert_eq!(p, path::Path::new("../does/not/exist"))
}
e => panic!("Got unexpected error {:?}", e),
}
}
#[test]
fn relative_rejects_relative_path_escaping_prefix() {
let err = Relative::new("inside/../../outside")
.err()
.expect("Parsing escaping path should have failed!");
match err {
Error::RelativePathEscapesPrefix(p) => {
assert_eq!(p, path::Path::new("inside/../../outside"))
}
e => panic!("Got unexpected error {:?}", e),
}
}
#[test]
fn relative_rejects_fully_absolute_path() {
let err = Relative::new("/absolute/path")
.err()
.expect("Parsing an absolute path should have failed!");
match err {
Error::PathIsAbsolute(p) => {
assert_eq!(p, path::Path::new("/absolute/path"))
}
e => panic!("Got unexpected error {:?}", e),
}
}
}