uv/crates/puffin/tests/venv.rs

490 lines
14 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#![cfg(feature = "python")]
use std::process::Command;
use anyhow::Result;
use assert_fs::prelude::*;
use puffin_fs::Normalized;
use crate::common::{create_bin_with_executables, get_bin, puffin_snapshot, EXCLUDE_NEWER};
mod common;
#[test]
fn create_venv() -> Result<()> {
let temp_dir = assert_fs::TempDir::new()?;
let cache_dir = assert_fs::TempDir::new()?;
let bin = create_bin_with_executables(&temp_dir, &["3.12"]).expect("Failed to create bin dir");
let venv = temp_dir.child(".venv");
// Create a virtual environment at `.venv`.
let filter_venv = regex::escape(&venv.normalized_display().to_string());
let filters = &[
(
r"Using Python 3\.\d+\.\d+ interpreter at .+",
"Using Python [VERSION] interpreter at [PATH]",
),
(&filter_venv, "/home/ferris/project/.venv"),
];
puffin_snapshot!(filters, Command::new(get_bin())
.arg("venv")
.arg(venv.as_os_str())
.arg("--python")
.arg("3.12")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("PUFFIN_TEST_PYTHON_PATH", bin.clone())
.current_dir(&temp_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python [VERSION] interpreter at [PATH]
Creating virtualenv at: /home/ferris/project/.venv
"###
);
venv.assert(predicates::path::is_dir());
// Create a virtual environment at the same location, which should replace it.
let filter_venv = regex::escape(&venv.normalized_display().to_string());
let filters = &[
(
r"Using Python 3\.\d+\.\d+ interpreter at .+",
"Using Python [VERSION] interpreter at [PATH]",
),
(&filter_venv, "/home/ferris/project/.venv"),
];
puffin_snapshot!(filters, Command::new(get_bin())
.arg("venv")
.arg(venv.as_os_str())
.arg("--python")
.arg("3.12")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("PUFFIN_NO_WRAP", "1")
.env("PUFFIN_TEST_PYTHON_PATH", bin)
.current_dir(&temp_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python [VERSION] interpreter at [PATH]
Creating virtualenv at: /home/ferris/project/.venv
"###
);
venv.assert(predicates::path::is_dir());
Ok(())
}
#[test]
fn create_venv_defaults_to_cwd() -> Result<()> {
let temp_dir = assert_fs::TempDir::new()?;
let cache_dir = assert_fs::TempDir::new()?;
let bin = create_bin_with_executables(&temp_dir, &["3.12"]).expect("Failed to create bin dir");
let venv = temp_dir.child(".venv");
let filter_venv = regex::escape(&venv.normalized_display().to_string());
let filters = &[
(
r"Using Python 3\.\d+\.\d+ interpreter at .+",
"Using Python [VERSION] interpreter at [PATH]",
),
(&filter_venv, "/home/ferris/project/.venv"),
];
puffin_snapshot!(filters, Command::new(get_bin())
.arg("venv")
.arg("--python")
.arg("3.12")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("PUFFIN_NO_WRAP", "1")
.env("PUFFIN_TEST_PYTHON_PATH", bin)
.current_dir(&temp_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python [VERSION] interpreter at [PATH]
Creating virtualenv at: .venv
"###
);
venv.assert(predicates::path::is_dir());
Ok(())
}
#[test]
fn seed() -> Result<()> {
let temp_dir = assert_fs::TempDir::new()?;
let cache_dir = assert_fs::TempDir::new()?;
let bin = create_bin_with_executables(&temp_dir, &["3.12"]).expect("Failed to create bin dir");
let venv = temp_dir.child(".venv");
let filter_venv = regex::escape(&venv.normalized_display().to_string());
let filters = &[
(
r"Using Python 3\.\d+\.\d+ interpreter at .+",
"Using Python [VERSION] interpreter at [PATH]",
),
(&filter_venv, "/home/ferris/project/.venv"),
];
puffin_snapshot!(filters, Command::new(get_bin())
.arg("venv")
.arg(venv.as_os_str())
.arg("--seed")
.arg("--python")
.arg("3.12")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("PUFFIN_NO_WRAP", "1")
.env("PUFFIN_TEST_PYTHON_PATH", bin)
.current_dir(&temp_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python [VERSION] interpreter at [PATH]
Creating virtualenv at: /home/ferris/project/.venv
+ setuptools==68.2.2
+ pip==23.3.1
+ wheel==0.41.3
"###
);
venv.assert(predicates::path::is_dir());
Ok(())
}
#[test]
fn create_venv_unknown_python_minor() -> Result<()> {
let temp_dir = assert_fs::TempDir::new()?;
let cache_dir = assert_fs::TempDir::new()?;
let bin = create_bin_with_executables(&temp_dir, &["3.12"]).expect("Failed to create bin dir");
let venv = temp_dir.child(".venv");
let mut command = Command::new(get_bin());
command
.arg("venv")
.arg(venv.as_os_str())
.arg("--python")
.arg("3.15")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("PUFFIN_NO_WRAP", "1")
.env("PUFFIN_TEST_PYTHON_PATH", bin)
.current_dir(&temp_dir);
if cfg!(windows) {
puffin_snapshot!(&mut command, @r###"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
× No Python 3.15 found through `py --list-paths`. Is Python 3.15 installed?
"###
);
} else {
puffin_snapshot!(&mut command, @r###"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
× No Python 3.15 In `PATH`. Is Python 3.15 installed?
"###
);
}
venv.assert(predicates::path::missing());
Ok(())
}
#[test]
#[cfg(unix)] // TODO(konstin): Support patch versions on Windows
fn create_venv_unknown_python_patch() -> Result<()> {
let temp_dir = assert_fs::TempDir::new()?;
let cache_dir = assert_fs::TempDir::new()?;
let bin = create_bin_with_executables(&temp_dir, &["3.12"]).expect("Failed to create bin dir");
let venv = temp_dir.child(".venv");
let filter_venv = regex::escape(&venv.normalized_display().to_string());
let filters = &[
(
r"Using Python 3\.\d+\.\d+ interpreter at .+",
"Using Python [VERSION] interpreter at [PATH]",
),
(&filter_venv, "/home/ferris/project/.venv"),
];
puffin_snapshot!(filters, Command::new(get_bin())
.arg("venv")
.arg(venv.as_os_str())
.arg("--python")
.arg("3.8.0")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("PUFFIN_NO_WRAP", "1")
.env("PUFFIN_TEST_PYTHON_PATH", bin)
.current_dir(&temp_dir), @r###"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
× No Python 3.8.0 In `PATH`. Is Python 3.8.0 installed?
"###
);
venv.assert(predicates::path::missing());
Ok(())
}
#[test]
#[ignore] // TODO(konstin): Switch patch version strategy
#[cfg(unix)] // TODO(konstin): Support patch versions on Windows
fn create_venv_python_patch() -> Result<()> {
let temp_dir = assert_fs::TempDir::new()?;
let cache_dir = assert_fs::TempDir::new()?;
let bin =
create_bin_with_executables(&temp_dir, &["3.12.1"]).expect("Failed to create bin dir");
let venv = temp_dir.child(".venv");
let filter_venv = regex::escape(&venv.normalized_display().to_string());
let filters = &[
(r"interpreter at .+", "interpreter at [PATH]"),
(&filter_venv, "/home/ferris/project/.venv"),
];
puffin_snapshot!(filters, Command::new(get_bin())
.arg("venv")
.arg(venv.as_os_str())
.arg("--python")
.arg("3.12.1")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("PUFFIN_NO_WRAP", "1")
.env("PUFFIN_TEST_PYTHON_PATH", bin)
.current_dir(&temp_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.12.1 interpreter at [PATH]
Creating virtualenv at: /home/ferris/project/.venv
"###
);
venv.assert(predicates::path::is_dir());
Ok(())
}
#[test]
fn file_exists() -> Result<()> {
let temp_dir = assert_fs::TempDir::new()?;
let cache_dir = assert_fs::TempDir::new()?;
let bin = create_bin_with_executables(&temp_dir, &["3.12"]).expect("Failed to create bin dir");
let venv = temp_dir.child(".venv");
// Create a file at `.venv`. Creating a virtualenv at the same path should fail.
venv.touch()?;
let filter_venv = regex::escape(&venv.normalized_display().to_string());
let filters = &[
(
r"Using Python 3\.\d+\.\d+ interpreter at .+",
"Using Python [VERSION] interpreter at [PATH]",
),
(&filter_venv, "/home/ferris/project/.venv"),
];
puffin_snapshot!(filters, Command::new(get_bin())
.arg("venv")
.arg(venv.as_os_str())
.arg("--python")
.arg("3.12")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("PUFFIN_NO_WRAP", "1")
.env("PUFFIN_TEST_PYTHON_PATH", bin)
.current_dir(&temp_dir), @r###"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
Using Python [VERSION] interpreter at [PATH]
Creating virtualenv at: /home/ferris/project/.venv
puffin::venv::creation
× Failed to create virtualenv
╰─▶ File exists at `/home/ferris/project/.venv`
"###
);
Ok(())
}
#[test]
fn empty_dir_exists() -> Result<()> {
let temp_dir = assert_fs::TempDir::new()?;
let cache_dir = assert_fs::TempDir::new()?;
let bin = create_bin_with_executables(&temp_dir, &["3.12"]).expect("Failed to create bin dir");
let venv = temp_dir.child(".venv");
// Create an empty directory at `.venv`. Creating a virtualenv at the same path should succeed.
venv.create_dir_all()?;
let filter_venv = regex::escape(&venv.normalized_display().to_string());
let filters = &[
(
r"Using Python 3\.\d+\.\d+ interpreter at .+",
"Using Python [VERSION] interpreter at [PATH]",
),
(&filter_venv, "/home/ferris/project/.venv"),
];
puffin_snapshot!(filters, Command::new(get_bin())
.arg("venv")
.arg(venv.as_os_str())
.arg("--python")
.arg("3.12")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("PUFFIN_NO_WRAP", "1")
.env("PUFFIN_TEST_PYTHON_PATH", bin)
.current_dir(&temp_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python [VERSION] interpreter at [PATH]
Creating virtualenv at: /home/ferris/project/.venv
"###
);
venv.assert(predicates::path::is_dir());
Ok(())
}
#[test]
fn non_empty_dir_exists() -> Result<()> {
let temp_dir = assert_fs::TempDir::new()?;
let cache_dir = assert_fs::TempDir::new()?;
let bin = create_bin_with_executables(&temp_dir, &["3.12"]).expect("Failed to create bin dir");
let venv = temp_dir.child(".venv");
// Create a non-empty directory at `.venv`. Creating a virtualenv at the same path should fail.
venv.create_dir_all()?;
venv.child("file").touch()?;
let filter_venv = regex::escape(&venv.normalized_display().to_string());
let filters = &[
(
r"Using Python 3\.\d+\.\d+ interpreter at .+",
"Using Python [VERSION] interpreter at [PATH]",
),
(&filter_venv, "/home/ferris/project/.venv"),
];
puffin_snapshot!(filters, Command::new(get_bin())
.arg("venv")
.arg(venv.as_os_str())
.arg("--python")
.arg("3.12")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("PUFFIN_NO_WRAP", "1")
.env("PUFFIN_TEST_PYTHON_PATH", bin)
.current_dir(&temp_dir), @r###"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
Using Python [VERSION] interpreter at [PATH]
Creating virtualenv at: /home/ferris/project/.venv
puffin::venv::creation
× Failed to create virtualenv
╰─▶ The directory `/home/ferris/project/.venv` exists, but it's not a virtualenv
"###
);
Ok(())
}
#[test]
fn virtualenv_compatibility() -> Result<()> {
let temp_dir = assert_fs::TempDir::new()?;
let cache_dir = assert_fs::TempDir::new()?;
let bin = create_bin_with_executables(&temp_dir, &["3.12"]).expect("Failed to create bin dir");
let venv = temp_dir.child(".venv");
// Create a virtual environment at `.venv`, passing the redundant `--clear` flag.
let filter_venv = regex::escape(&venv.normalized_display().to_string());
let filters = &[
(
r"Using Python 3\.\d+\.\d+ interpreter at .+",
"Using Python [VERSION] interpreter at [PATH]",
),
(&filter_venv, "/home/ferris/project/.venv"),
];
puffin_snapshot!(filters, Command::new(get_bin())
.arg("venv")
.arg(venv.as_os_str())
.arg("--clear")
.arg("--python")
.arg("3.12")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("PUFFIN_TEST_PYTHON_PATH", bin.clone())
.current_dir(&temp_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
warning: virtualenv's `--clear` has no effect (Puffin always clears the virtual environment).
Using Python [VERSION] interpreter at [PATH]
Creating virtualenv at: /home/ferris/project/.venv
"###
);
venv.assert(predicates::path::is_dir());
Ok(())
}