Skip to content

Commit

Permalink
chore(tests): add .use_symlinked_temp_dir() to TestBuilder (#19435)
Browse files Browse the repository at this point in the history
This allows easily using a symlinked temporary directory, which is
useful for debugging issues locally that happen on the CI with a
symlinked temporary directory. For example:

```rs
let context = TestContextBuilder::new()
    .use_temp_cwd()
    .use_symlinked_temp_dir() // add this
    .build();
```
  • Loading branch information
dsherret authored and bartlomieju committed Jun 15, 2023
1 parent 40cd7a3 commit 573cf00
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 11 deletions.
18 changes: 18 additions & 0 deletions test_util/src/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion test_util/src/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!({}));
Expand Down
76 changes: 66 additions & 10 deletions test_util/src/temp_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,54 @@

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<TempDirInner>,
target: Arc<TempDirInner>,
},
}

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.
///
/// 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<tempfile::TempDir>);
pub struct TempDir(Arc<TempDirInner>);

impl Default for TempDir {
fn default() -> Self {
Expand All @@ -35,50 +70,71 @@ 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();
builder.prefix(prefix.unwrap_or("deno-cli-test"));
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 {
Url::from_directory_path(self.path()).unwrap()
}

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<Path>) {
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<Path>) {
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<Path>) {
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<Path>) -> 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<Path>, to: impl AsRef<Path>) {
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<Path>, text: impl AsRef<str>) {
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(
Expand Down

0 comments on commit 573cf00

Please sign in to comment.