1f916e65cf
helix-stdx is meant to carry extensions to the stdlib or low-level dependencies that are useful in all other crates. This commit starts with all of the path functions from helix-core and the CWD tracking that lived in helix-loader. The CWD tracking in helix-loader was previously unable to call the canonicalization functions in helix-core. Switching to our custom canonicalization code should make no noticeable difference though since `std::env::current_dir` returns a canonicalized path with symlinks resolved (at least on unix).
124 lines
3.3 KiB
Rust
124 lines
3.3 KiB
Rust
#![cfg(windows)]
|
|
|
|
use std::{
|
|
env::set_current_dir,
|
|
error::Error,
|
|
path::{Component, Path, PathBuf},
|
|
};
|
|
|
|
use helix_stdx::path;
|
|
use tempfile::Builder;
|
|
|
|
// Paths on Windows are almost always case-insensitive.
|
|
// Normalization should return the original path.
|
|
// E.g. mkdir `CaSe`, normalize(`case`) = `CaSe`.
|
|
#[test]
|
|
fn test_case_folding_windows() -> Result<(), Box<dyn Error>> {
|
|
// tmp/root/case
|
|
let tmp_prefix = std::env::temp_dir();
|
|
set_current_dir(&tmp_prefix)?;
|
|
|
|
let root = Builder::new().prefix("root-").tempdir()?;
|
|
let case = Builder::new().prefix("CaSe-").tempdir_in(&root)?;
|
|
|
|
let root_without_prefix = root.path().strip_prefix(&tmp_prefix)?;
|
|
|
|
let lowercase_case = format!(
|
|
"case-{}",
|
|
case.path()
|
|
.file_name()
|
|
.unwrap()
|
|
.to_string_lossy()
|
|
.split_at(5)
|
|
.1
|
|
);
|
|
let test_path = root_without_prefix.join(lowercase_case);
|
|
assert_eq!(
|
|
path::normalize(&test_path),
|
|
case.path().strip_prefix(&tmp_prefix)?
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_normalize_path() -> Result<(), Box<dyn Error>> {
|
|
/*
|
|
tmp/root/
|
|
├── link -> dir1/orig_file
|
|
├── dir1/
|
|
│ └── orig_file
|
|
└── dir2/
|
|
└── dir_link -> ../dir1/
|
|
*/
|
|
|
|
let tmp_prefix = std::env::temp_dir();
|
|
set_current_dir(&tmp_prefix)?;
|
|
|
|
// Create a tree structure as shown above
|
|
let root = Builder::new().prefix("root-").tempdir()?;
|
|
let dir1 = Builder::new().prefix("dir1-").tempdir_in(&root)?;
|
|
let orig_file = Builder::new().prefix("orig_file-").tempfile_in(&dir1)?;
|
|
let dir2 = Builder::new().prefix("dir2-").tempdir_in(&root)?;
|
|
|
|
// Create path and delete existing file
|
|
let dir_link = Builder::new()
|
|
.prefix("dir_link-")
|
|
.tempfile_in(&dir2)?
|
|
.path()
|
|
.to_owned();
|
|
let link = Builder::new()
|
|
.prefix("link-")
|
|
.tempfile_in(&root)?
|
|
.path()
|
|
.to_owned();
|
|
|
|
use std::os::windows;
|
|
windows::fs::symlink_dir(&dir1, &dir_link)?;
|
|
windows::fs::symlink_file(&orig_file, &link)?;
|
|
|
|
// root/link
|
|
let path = link.strip_prefix(&tmp_prefix)?;
|
|
assert_eq!(
|
|
path::normalize(path),
|
|
path,
|
|
"input {:?} and symlink last component shouldn't be resolved",
|
|
path
|
|
);
|
|
|
|
// root/dir2/dir_link/orig_file/../..
|
|
let path = dir_link
|
|
.strip_prefix(&tmp_prefix)
|
|
.unwrap()
|
|
.join(orig_file.path().file_name().unwrap())
|
|
.join(Component::ParentDir)
|
|
.join(Component::ParentDir);
|
|
let expected = dir_link
|
|
.strip_prefix(&tmp_prefix)
|
|
.unwrap()
|
|
.join(Component::ParentDir);
|
|
assert_eq!(
|
|
path::normalize(&path),
|
|
expected,
|
|
"input {:?} and \"..\" should not erase the simlink that goes ahead",
|
|
&path
|
|
);
|
|
|
|
// root/link/.././../dir2/../
|
|
let path = link
|
|
.strip_prefix(&tmp_prefix)
|
|
.unwrap()
|
|
.join(Component::ParentDir)
|
|
.join(Component::CurDir)
|
|
.join(Component::ParentDir)
|
|
.join(dir2.path().file_name().unwrap())
|
|
.join(Component::ParentDir);
|
|
let expected = link
|
|
.strip_prefix(&tmp_prefix)
|
|
.unwrap()
|
|
.join(Component::ParentDir)
|
|
.join(Component::ParentDir);
|
|
assert_eq!(path::normalize(&path), expected, "input {:?}", &path);
|
|
|
|
Ok(())
|
|
}
|