]>
Commit | Line | Data |
---|---|---|
1 | use std::{ | |
2 | fs, io, | |
3 | path::{Path, PathBuf}, | |
4 | sync::atomic::{AtomicUsize, Ordering}, | |
5 | }; | |
6 | ||
7 | pub(crate) struct TestDir { | |
8 | path: PathBuf, | |
9 | keep: bool, | |
10 | } | |
11 | ||
12 | impl TestDir { | |
13 | pub(crate) fn new() -> TestDir { | |
14 | let temp_dir = std::env::temp_dir(); | |
15 | // On MacOS builders on GitHub actions, the temp dir is a symlink, and | |
16 | // that causes problems down the line. Specifically: | |
17 | // * Cargo may emit different PackageId depending on the working directory | |
18 | // * rust-analyzer may fail to map LSP URIs to correct paths. | |
19 | // | |
20 | // Work-around this by canonicalizing. Note that we don't want to do this | |
21 | // on *every* OS, as on windows `canonicalize` itself creates problems. | |
22 | #[cfg(target_os = "macos")] | |
23 | let temp_dir = temp_dir.canonicalize().unwrap(); | |
24 | ||
25 | let base = temp_dir.join("testdir"); | |
26 | let pid = std::process::id(); | |
27 | ||
28 | static CNT: AtomicUsize = AtomicUsize::new(0); | |
29 | for _ in 0..100 { | |
30 | let cnt = CNT.fetch_add(1, Ordering::Relaxed); | |
31 | let path = base.join(format!("{pid}_{cnt}")); | |
32 | if path.is_dir() { | |
33 | continue; | |
34 | } | |
35 | fs::create_dir_all(&path).unwrap(); | |
36 | return TestDir { path, keep: false }; | |
37 | } | |
38 | panic!("Failed to create a temporary directory") | |
39 | } | |
40 | #[allow(unused)] | |
41 | pub(crate) fn keep(mut self) -> TestDir { | |
42 | self.keep = true; | |
43 | self | |
44 | } | |
45 | pub(crate) fn path(&self) -> &Path { | |
46 | &self.path | |
47 | } | |
48 | } | |
49 | ||
50 | impl Drop for TestDir { | |
51 | fn drop(&mut self) { | |
52 | if self.keep { | |
53 | return; | |
54 | } | |
55 | remove_dir_all(&self.path).unwrap_or_else(|err| { | |
56 | panic!("failed to remove temporary directory {}: {err}", self.path.display()) | |
57 | }) | |
58 | } | |
59 | } | |
60 | ||
61 | #[cfg(not(windows))] | |
62 | fn remove_dir_all(path: &Path) -> io::Result<()> { | |
63 | fs::remove_dir_all(path) | |
64 | } | |
65 | ||
66 | #[cfg(windows)] | |
67 | fn remove_dir_all(path: &Path) -> io::Result<()> { | |
68 | for _ in 0..99 { | |
69 | if fs::remove_dir_all(path).is_ok() { | |
70 | return Ok(()); | |
71 | } | |
72 | std::thread::sleep(std::time::Duration::from_millis(10)) | |
73 | } | |
74 | fs::remove_dir_all(path) | |
75 | } |