rust/crates/rust-analyzer/tests/slow-tests/testdir.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

111 lines
3.3 KiB
Rust
Raw Normal View History

2020-07-23 15:26:25 -05:00
use std::{
fs, io,
path::{Path, PathBuf},
sync::atomic::{AtomicUsize, Ordering},
};
2020-11-02 09:31:38 -06:00
pub(crate) struct TestDir {
2020-07-23 15:26:25 -05:00
path: PathBuf,
keep: bool,
}
impl TestDir {
2020-11-02 09:31:38 -06:00
pub(crate) fn new() -> TestDir {
2024-01-18 06:59:49 -06:00
TestDir::new_dir(false)
}
pub(crate) fn new_symlink() -> TestDir {
2024-01-18 06:59:49 -06:00
TestDir::new_dir(true)
}
fn new_dir(symlink: bool) -> TestDir {
2021-07-19 08:24:45 -05:00
let temp_dir = std::env::temp_dir();
// On MacOS builders on GitHub actions, the temp dir is a symlink, and
// that causes problems down the line. Specifically:
// * Cargo may emit different PackageId depending on the working directory
// * rust-analyzer may fail to map LSP URIs to correct paths.
//
// Work-around this by canonicalizing. Note that we don't want to do this
// on *every* OS, as on windows `canonicalize` itself creates problems.
#[cfg(target_os = "macos")]
let temp_dir = temp_dir.canonicalize().unwrap();
let base = temp_dir.join("testdir");
2020-07-23 15:26:25 -05:00
let pid = std::process::id();
static CNT: AtomicUsize = AtomicUsize::new(0);
for _ in 0..100 {
let cnt = CNT.fetch_add(1, Ordering::Relaxed);
let path = base.join(format!("{pid}_{cnt}"));
2020-07-23 15:26:25 -05:00
if path.is_dir() {
continue;
}
fs::create_dir_all(&path).unwrap();
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
if symlink {
let symlink_path = base.join(format!("{pid}_{cnt}_symlink"));
#[cfg(any(target_os = "macos", target_os = "linux"))]
std::os::unix::fs::symlink(path, &symlink_path).unwrap();
#[cfg(target_os = "windows")]
std::os::windows::fs::symlink_dir(path, &symlink_path).unwrap();
return TestDir { path: symlink_path, keep: false };
}
2020-07-23 15:26:25 -05:00
return TestDir { path, keep: false };
}
panic!("Failed to create a temporary directory")
}
2020-07-23 15:26:25 -05:00
#[allow(unused)]
2020-11-02 09:31:38 -06:00
pub(crate) fn keep(mut self) -> TestDir {
2020-07-23 15:26:25 -05:00
self.keep = true;
self
}
2020-11-02 09:31:38 -06:00
pub(crate) fn path(&self) -> &Path {
2020-07-23 15:26:25 -05:00
&self.path
}
}
impl Drop for TestDir {
fn drop(&mut self) {
if self.keep {
return;
}
let filetype = fs::symlink_metadata(&self.path).unwrap().file_type();
let actual_path = filetype.is_symlink().then(|| fs::read_link(&self.path).unwrap());
if let Some(actual_path) = actual_path {
remove_dir_all(&actual_path).unwrap_or_else(|err| {
panic!(
"failed to remove temporary link to directory {}: {err}",
actual_path.display()
)
})
}
2022-01-01 08:43:02 -06:00
remove_dir_all(&self.path).unwrap_or_else(|err| {
panic!("failed to remove temporary directory {}: {err}", self.path.display())
});
2020-07-23 15:26:25 -05:00
}
}
#[cfg(not(windows))]
fn remove_dir_all(path: &Path) -> io::Result<()> {
fs::remove_dir_all(path)
}
#[cfg(windows)]
fn remove_dir_all(path: &Path) -> io::Result<()> {
for _ in 0..99 {
if fs::remove_dir_all(path).is_ok() {
return Ok(());
}
std::thread::sleep(std::time::Duration::from_millis(10))
}
fs::remove_dir_all(path)
}