diff --git a/test_util/src/builders.rs b/test_util/src/builders.rs index a000e5bcd3..049ccbd671 100644 --- a/test_util/src/builders.rs +++ b/test_util/src/builders.rs @@ -31,6 +31,7 @@ pub struct TestContextBuilder { use_http_server: bool, use_temp_cwd: bool, use_separate_deno_dir: bool, + use_symlinked_temp_dir: bool, /// Copies the files at the specified directory in the "testdata" directory /// to the temp folder and runs the test from there. This is useful when /// the test creates files in the testdata directory (ex. a node_modules folder) @@ -59,6 +60,18 @@ impl TestContextBuilder { self } + /// Causes the temp directory to be symlinked to a target directory + /// which is useful for debugging issues that only show up on the CI. + /// + /// Note: This method is not actually deprecated, it's just the CI + /// does this by default so there's no need to check in any code that + /// uses this into the repo. This is just for debugging purposes. + #[deprecated] + pub fn use_symlinked_temp_dir(mut self) -> Self { + self.use_symlinked_temp_dir = true; + self + } + /// By default, the temp_dir and the deno_dir will be shared. /// In some cases, that might cause an issue though, so calling /// this will use a separate directory for the deno dir and the @@ -110,6 +123,11 @@ impl TestContextBuilder { } else { deno_dir.clone() }; + let temp_dir = if self.use_symlinked_temp_dir { + TempDir::new_symlinked(temp_dir) + } else { + temp_dir + }; let testdata_dir = if let Some(temp_copy_dir) = &self.copy_temp_dir { let test_data_path = testdata_path().join(temp_copy_dir); let temp_copy_dir = temp_dir.path().join(temp_copy_dir); diff --git a/test_util/src/lsp.rs b/test_util/src/lsp.rs index 831df28e9e..87dfaf6f97 100644 --- a/test_util/src/lsp.rs +++ b/test_util/src/lsp.rs @@ -616,7 +616,7 @@ impl LspClient { config: Value, ) { let mut builder = InitializeParamsBuilder::new(); - builder.set_root_uri(self.context.deno_dir().uri()); + builder.set_root_uri(self.context.temp_dir().uri()); do_build(&mut builder); self.write_request("initialize", builder.build()); self.write_notification("initialized", json!({})); diff --git a/test_util/src/temp_dir.rs b/test_util/src/temp_dir.rs index f66bf1398b..4ec6eb9f8c 100644 --- a/test_util/src/temp_dir.rs +++ b/test_util/src/temp_dir.rs @@ -2,11 +2,46 @@ use std::fs; use std::path::Path; +use std::path::PathBuf; use std::sync::Arc; use anyhow::Context; use lsp_types::Url; +enum TempDirInner { + TempDir(tempfile::TempDir), + Path(PathBuf), + Symlinked { + symlink: Arc, + target: Arc, + }, +} + +impl TempDirInner { + pub fn path(&self) -> &Path { + match self { + Self::Path(path) => path.as_path(), + Self::TempDir(dir) => dir.path(), + Self::Symlinked { symlink, .. } => symlink.path(), + } + } + + pub fn target_path(&self) -> &Path { + match self { + TempDirInner::Symlinked { target, .. } => target.target_path(), + _ => self.path(), + } + } +} + +impl Drop for TempDirInner { + fn drop(&mut self) { + if let Self::Path(path) = self { + _ = fs::remove_dir_all(path); + } + } +} + /// For creating temporary directories in tests. /// /// This was done because `tempfiles::TempDir` was very slow on Windows. @@ -14,7 +49,7 @@ use lsp_types::Url; /// Note: Do not use this in actual code as this does not protect against /// "insecure temporary file" security vulnerabilities. #[derive(Clone)] -pub struct TempDir(Arc); +pub struct TempDir(Arc); impl Default for TempDir { fn default() -> Self { @@ -35,6 +70,23 @@ impl TempDir { Self::new_inner(&std::env::temp_dir(), Some(prefix)) } + pub fn new_with_path(path: &Path) -> Self { + Self(Arc::new(TempDirInner::Path(path.to_path_buf()))) + } + + pub fn new_symlinked(target: TempDir) -> Self { + let target_path = target.path(); + let path = target_path.parent().unwrap().join(format!( + "{}_symlinked", + target_path.file_name().unwrap().to_str().unwrap() + )); + target.symlink_dir(target.path(), &path); + TempDir(Arc::new(TempDirInner::Symlinked { + target: target.0, + symlink: Self::new_with_path(&path).0, + })) + } + /// Create a new temporary directory with the given prefix as part of its name, if specified. fn new_inner(parent_dir: &Path, prefix: Option<&str>) -> Self { let mut builder = tempfile::Builder::new(); @@ -42,7 +94,7 @@ impl TempDir { let dir = builder .tempdir_in(parent_dir) .expect("Failed to create a temporary directory"); - Self(dir.into()) + Self(Arc::new(TempDirInner::TempDir(dir))) } pub fn uri(&self) -> Url { @@ -50,35 +102,39 @@ impl TempDir { } pub fn path(&self) -> &Path { - let inner = &self.0; - inner.path() + self.0.path() + } + + /// The resolved final target path if this is a symlink. + pub fn target_path(&self) -> &Path { + self.0.target_path() } pub fn create_dir_all(&self, path: impl AsRef) { - fs::create_dir_all(self.path().join(path)).unwrap(); + fs::create_dir_all(self.target_path().join(path)).unwrap(); } pub fn remove_file(&self, path: impl AsRef) { - fs::remove_file(self.path().join(path)).unwrap(); + fs::remove_file(self.target_path().join(path)).unwrap(); } pub fn remove_dir_all(&self, path: impl AsRef) { - fs::remove_dir_all(self.path().join(path)).unwrap(); + fs::remove_dir_all(self.target_path().join(path)).unwrap(); } pub fn read_to_string(&self, path: impl AsRef) -> String { - let file_path = self.path().join(path); + let file_path = self.target_path().join(path); fs::read_to_string(&file_path) .with_context(|| format!("Could not find file: {}", file_path.display())) .unwrap() } pub fn rename(&self, from: impl AsRef, to: impl AsRef) { - fs::rename(self.path().join(from), self.path().join(to)).unwrap(); + fs::rename(self.target_path().join(from), self.path().join(to)).unwrap(); } pub fn write(&self, path: impl AsRef, text: impl AsRef) { - fs::write(self.path().join(path), text.as_ref()).unwrap(); + fs::write(self.target_path().join(path), text.as_ref()).unwrap(); } pub fn symlink_dir(