mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-02 21:02:37 +00:00
uv/tests: add and rejigger some helpers in our common test library
This adds some variations on 'uv add' and 'uv remove' specifically for testing changes to the lock file (and not anything else). We also rejigger 'run_and_format' so that we can use it in other contexts, particularly for error reporting. And we add a 'diff_lock' helper for returning the changes made to a lock file after running a command.
This commit is contained in:
parent
c7218e19ac
commit
2b68a3d17a
1 changed files with 79 additions and 3 deletions
|
|
@ -6,7 +6,7 @@ use std::env;
|
|||
use std::ffi::OsString;
|
||||
use std::iter::Iterator;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Output};
|
||||
use std::process::{Command, ExitStatus, Output};
|
||||
use std::str::FromStr;
|
||||
|
||||
use assert_cmd::assert::{Assert, OutputAssertExt};
|
||||
|
|
@ -559,6 +559,14 @@ impl TestContext {
|
|||
command
|
||||
}
|
||||
|
||||
/// Create a `uv add --no-sync` command for the given requirements.
|
||||
pub fn add_no_sync(&self, reqs: &[&str]) -> Command {
|
||||
let mut command = Command::new(get_bin());
|
||||
command.arg("add").arg("--no-sync").args(reqs);
|
||||
self.add_shared_args(&mut command);
|
||||
command
|
||||
}
|
||||
|
||||
/// Create a `uv remove` command for the given requirements.
|
||||
pub fn remove(&self, reqs: &[&str]) -> Command {
|
||||
let mut command = Command::new(get_bin());
|
||||
|
|
@ -567,6 +575,14 @@ impl TestContext {
|
|||
command
|
||||
}
|
||||
|
||||
/// Create a `uv remove --no-sync` command for the given requirements.
|
||||
pub fn remove_no_sync(&self, reqs: &[&str]) -> Command {
|
||||
let mut command = Command::new(get_bin());
|
||||
command.arg("remove").arg("--no-sync").args(reqs);
|
||||
self.add_shared_args(&mut command);
|
||||
command
|
||||
}
|
||||
|
||||
/// Create a `uv tree` command with options shared across scenarios.
|
||||
pub fn tree(&self) -> Command {
|
||||
let mut command = Command::new(get_bin());
|
||||
|
|
@ -738,6 +754,51 @@ impl TestContext {
|
|||
let project_dir = PathBuf::from(format!("../../ecosystem/{name}"));
|
||||
self.temp_dir.copy_from(project_dir, &["*"]).unwrap();
|
||||
}
|
||||
|
||||
/// Creates a way to compare the changes made to a lock file.
|
||||
///
|
||||
/// This routine starts by copying (not moves) the generated lock file to
|
||||
/// memory. It then calls the given closure with this test context to get a
|
||||
/// `Command` and runs the command. The diff between the old lock file and
|
||||
/// the new one is then returned.
|
||||
///
|
||||
/// This assumes that a lock has already been performed.
|
||||
pub fn diff_lock(&self, change: impl Fn(&TestContext) -> Command) -> String {
|
||||
static TRIM_TRAILING_WHITESPACE: std::sync::LazyLock<Regex> =
|
||||
std::sync::LazyLock::new(|| Regex::new(r"(?m)^\s+$").unwrap());
|
||||
|
||||
let lock_path = ChildPath::new(self.temp_dir.join("uv.lock"));
|
||||
let old_lock = fs_err::read_to_string(&lock_path).unwrap();
|
||||
let (snapshot, _, status) = run_and_format_with_status(
|
||||
change(self),
|
||||
self.filters(),
|
||||
"diff_lock",
|
||||
Some(crate::common::WindowsFilters::Platform),
|
||||
);
|
||||
assert!(status.success(), "{snapshot}");
|
||||
let new_lock = fs_err::read_to_string(&lock_path).unwrap();
|
||||
diff_snapshot(&old_lock, &new_lock)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a "unified" diff between the two line-oriented strings suitable
|
||||
/// for snapshotting.
|
||||
pub fn diff_snapshot(old: &str, new: &str) -> String {
|
||||
static TRIM_TRAILING_WHITESPACE: std::sync::LazyLock<Regex> =
|
||||
std::sync::LazyLock::new(|| Regex::new(r"(?m)^\s+$").unwrap());
|
||||
|
||||
let diff = similar::TextDiff::from_lines(old, new);
|
||||
let unified = diff
|
||||
.unified_diff()
|
||||
.context_radius(10)
|
||||
.header("old", "new")
|
||||
.to_string();
|
||||
// Not totally clear why, but some lines end up containing only
|
||||
// whitespace in the diff, even though they don't appear in the
|
||||
// original data. So just strip them here.
|
||||
TRIM_TRAILING_WHITESPACE
|
||||
.replace_all(&unified, "")
|
||||
.into_owned()
|
||||
}
|
||||
|
||||
pub fn site_packages_path(venv: &Path, python: &str) -> PathBuf {
|
||||
|
|
@ -869,11 +930,25 @@ pub enum WindowsFilters {
|
|||
///
|
||||
/// This function is derived from `insta_cmd`s `spawn_with_info`.
|
||||
pub fn run_and_format<T: AsRef<str>>(
|
||||
mut command: impl BorrowMut<Command>,
|
||||
command: impl BorrowMut<Command>,
|
||||
filters: impl AsRef<[(T, T)]>,
|
||||
function_name: &str,
|
||||
windows_filters: Option<WindowsFilters>,
|
||||
) -> (String, Output) {
|
||||
let (snapshot, output, _) =
|
||||
run_and_format_with_status(command, filters, function_name, windows_filters);
|
||||
(snapshot, output)
|
||||
}
|
||||
|
||||
/// Execute the command and format its output status, stdout and stderr into a snapshot string.
|
||||
///
|
||||
/// This function is derived from `insta_cmd`s `spawn_with_info`.
|
||||
pub fn run_and_format_with_status<T: AsRef<str>>(
|
||||
mut command: impl BorrowMut<Command>,
|
||||
filters: impl AsRef<[(T, T)]>,
|
||||
function_name: &str,
|
||||
windows_filters: Option<WindowsFilters>,
|
||||
) -> (String, Output, ExitStatus) {
|
||||
let program = command
|
||||
.borrow_mut()
|
||||
.get_program()
|
||||
|
|
@ -955,7 +1030,8 @@ pub fn run_and_format<T: AsRef<str>>(
|
|||
}
|
||||
}
|
||||
|
||||
(snapshot, output)
|
||||
let status = output.status;
|
||||
(snapshot, output, status)
|
||||
}
|
||||
|
||||
/// Recursively copy a directory and its contents.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue