Store test temporary directories outside of /tmp (#6559)

## Summary

There's a long comment inline describing the motivation here.
This commit is contained in:
Charlie Marsh 2024-08-23 21:51:32 -04:00 committed by GitHub
parent a959772074
commit 44e36a7e69
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 49 additions and 11 deletions

2
Cargo.lock generated
View file

@ -4504,6 +4504,7 @@ dependencies = [
"cache-key", "cache-key",
"clap", "clap",
"distribution-types", "distribution-types",
"etcetera",
"filetime", "filetime",
"flate2", "flate2",
"fs-err", "fs-err",
@ -4531,6 +4532,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"similar", "similar",
"tempfile",
"textwrap", "textwrap",
"thiserror", "thiserror",
"tikv-jemallocator", "tikv-jemallocator",

View file

@ -86,6 +86,7 @@ assert_cmd = { version = "2.0.14" }
assert_fs = { version = "1.1.0" } assert_fs = { version = "1.1.0" }
base64 = { version = "0.22.0" } base64 = { version = "0.22.0" }
byteorder = { version = "1.5.0" } byteorder = { version = "1.5.0" }
etcetera = { workspace = true }
filetime = { version = "0.2.23" } filetime = { version = "0.2.23" }
ignore = { version = "0.4.22" } ignore = { version = "0.4.22" }
indoc = { version = "2.0.4" } indoc = { version = "2.0.4" }
@ -94,6 +95,7 @@ predicates = { version = "3.0.4" }
regex = { workspace = true } regex = { workspace = true }
reqwest = { workspace = true, features = ["blocking"], default-features = false } reqwest = { workspace = true, features = ["blocking"], default-features = false }
similar = { version = "2.6.0" } similar = { version = "2.6.0" }
tempfile = { workspace = true }
zip = { workspace = true } zip = { workspace = true }
[package.metadata.cargo-shear] [package.metadata.cargo-shear]

View file

@ -13,6 +13,7 @@ use assert_cmd::assert::{Assert, OutputAssertExt};
use assert_fs::assert::PathAssert; use assert_fs::assert::PathAssert;
use assert_fs::fixture::{ChildPath, PathChild, PathCopy, PathCreateDir, SymlinkToFile}; use assert_fs::fixture::{ChildPath, PathChild, PathCopy, PathCreateDir, SymlinkToFile};
use base64::{prelude::BASE64_STANDARD as base64, Engine}; use base64::{prelude::BASE64_STANDARD as base64, Engine};
use etcetera::BaseStrategy;
use indoc::formatdoc; use indoc::formatdoc;
use itertools::Itertools; use itertools::Itertools;
use predicates::prelude::predicate; use predicates::prelude::predicate;
@ -72,10 +73,10 @@ pub const INSTA_FILTERS: &[(&str, &str)] = &[
/// * Set a cutoff for versions used in the resolution so the snapshots don't change after a new release. /// * Set a cutoff for versions used in the resolution so the snapshots don't change after a new release.
/// * Set the venv to a fresh `.venv` in `temp_dir` /// * Set the venv to a fresh `.venv` in `temp_dir`
pub struct TestContext { pub struct TestContext {
pub temp_dir: assert_fs::TempDir, pub temp_dir: ChildPath,
pub cache_dir: assert_fs::TempDir, pub cache_dir: ChildPath,
pub python_dir: assert_fs::TempDir, pub python_dir: ChildPath,
pub home_dir: assert_fs::TempDir, pub home_dir: ChildPath,
pub venv: ChildPath, pub venv: ChildPath,
pub workspace_root: PathBuf, pub workspace_root: PathBuf,
@ -87,6 +88,9 @@ pub struct TestContext {
/// Standard filters for this test context. /// Standard filters for this test context.
filters: Vec<(String, String)>, filters: Vec<(String, String)>,
#[allow(dead_code)]
_root: tempfile::TempDir,
} }
impl TestContext { impl TestContext {
@ -168,10 +172,39 @@ impl TestContext {
/// ///
/// See [`TestContext::new`] if only a single version is desired. /// See [`TestContext::new`] if only a single version is desired.
pub fn new_with_versions(python_versions: &[&str]) -> Self { pub fn new_with_versions(python_versions: &[&str]) -> Self {
let temp_dir = assert_fs::TempDir::new().expect("Failed to create test working directory"); // Discover the path to the XDG state directory. We use this, rather than the OS-specific
let cache_dir = assert_fs::TempDir::new().expect("Failed to create test cache directory"); // temporary directory, because on macOS (and Windows on GitHub Actions), they involve
let python_dir = assert_fs::TempDir::new().expect("Failed to create test Python directory"); // symlinks. (On macOS, the temporary directory is, like `/var/...`, which resolves to
let home_dir = assert_fs::TempDir::new().expect("Failed to create test home directory"); // `/private/var/...`.)
//
// It turns out that, at least on macOS, if we pass a symlink as `current_dir`, it gets
// _immediately_ resolved (such that if you call `current_dir` in the running `Command`, it
// returns resolved symlink). This is problematic, as we _don't_ want to resolve symlinks
// for user-provided paths.
let bucket = env::var("UV_INTERNAL__TEST_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| {
etcetera::base_strategy::choose_base_strategy()
.expect("Failed to find base strategy")
.data_dir()
.join("uv")
.join("tests")
});
fs_err::create_dir_all(&bucket).expect("Failed to create test bucket");
let root = tempfile::TempDir::new_in(bucket).expect("Failed to create test root directory");
let temp_dir = ChildPath::new(root.path()).child("temp");
fs_err::create_dir_all(&temp_dir).expect("Failed to create test working directory");
let cache_dir = ChildPath::new(root.path()).child("cache");
fs_err::create_dir_all(&cache_dir).expect("Failed to create test cache directory");
let python_dir = ChildPath::new(root.path()).child("python");
fs_err::create_dir_all(&python_dir).expect("Failed to create test Python directory");
let home_dir = ChildPath::new(root.path()).child("home");
fs_err::create_dir_all(&home_dir).expect("Failed to create test home directory");
// Canonicalize the temp dir for consistent snapshot behavior // Canonicalize the temp dir for consistent snapshot behavior
let canonical_temp_dir = temp_dir.canonicalize().unwrap(); let canonical_temp_dir = temp_dir.canonicalize().unwrap();
@ -331,6 +364,7 @@ impl TestContext {
python_version, python_version,
python_versions, python_versions,
filters, filters,
_root: root,
} }
} }
@ -852,7 +886,7 @@ pub fn get_python(version: &PythonVersion) -> PathBuf {
/// Create a virtual environment at the given path. /// Create a virtual environment at the given path.
pub fn create_venv_from_executable<P: AsRef<std::path::Path>>( pub fn create_venv_from_executable<P: AsRef<std::path::Path>>(
path: P, path: P,
cache_dir: &assert_fs::TempDir, cache_dir: &ChildPath,
python: &Path, python: &Path,
) { ) {
assert_cmd::Command::new(get_bin()) assert_cmd::Command::new(get_bin())
@ -879,7 +913,7 @@ pub fn get_bin() -> PathBuf {
/// ///
/// Generally this should be used with `UV_TEST_PYTHON_PATH`. /// Generally this should be used with `UV_TEST_PYTHON_PATH`.
pub fn python_path_with_versions( pub fn python_path_with_versions(
temp_dir: &assert_fs::TempDir, temp_dir: &ChildPath,
python_versions: &[&str], python_versions: &[&str],
) -> anyhow::Result<OsString> { ) -> anyhow::Result<OsString> {
Ok(std::env::join_paths( Ok(std::env::join_paths(
@ -893,7 +927,7 @@ pub fn python_path_with_versions(
/// ///
/// Generally this should be used with `UV_TEST_PYTHON_PATH`. /// Generally this should be used with `UV_TEST_PYTHON_PATH`.
pub fn python_installations_for_versions( pub fn python_installations_for_versions(
temp_dir: &assert_fs::TempDir, temp_dir: &ChildPath,
python_versions: &[&str], python_versions: &[&str],
) -> anyhow::Result<Vec<PathBuf>> { ) -> anyhow::Result<Vec<PathBuf>> {
let cache = Cache::from_path(temp_dir.child("cache").to_path_buf()).init()?; let cache = Cache::from_path(temp_dir.child("cache").to_path_buf()).init()?;