mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Unify test venv python
command creation (#14117)
Refactoring in preparation for https://github.com/astral-sh/uv/pull/14080
This commit is contained in:
parent
4d9c9a1e76
commit
ee0ba65eb2
10 changed files with 212 additions and 811 deletions
|
@ -7,7 +7,6 @@ use indoc::indoc;
|
|||
use insta::assert_snapshot;
|
||||
use predicates::prelude::predicate;
|
||||
use std::env::current_dir;
|
||||
use std::process::Command;
|
||||
use zip::ZipArchive;
|
||||
|
||||
#[test]
|
||||
|
@ -1857,7 +1856,7 @@ fn build_unconfigured_setuptools() -> Result<()> {
|
|||
+ greet==0.1.0 (from file://[TEMP_DIR]/)
|
||||
"###);
|
||||
|
||||
uv_snapshot!(context.filters(), Command::new(context.interpreter()).arg("-c").arg("import greet"), @r###"
|
||||
uv_snapshot!(context.filters(), context.python_command().arg("-c").arg("import greet"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
|
|
@ -50,13 +50,9 @@ fn built_by_uv_direct_wheel() -> Result<()> {
|
|||
.assert()
|
||||
.success();
|
||||
|
||||
uv_snapshot!(context
|
||||
.run()
|
||||
.arg("python")
|
||||
uv_snapshot!(context.python_command()
|
||||
.arg("-c")
|
||||
.arg(BUILT_BY_UV_TEST_SCRIPT)
|
||||
// Python on windows
|
||||
.env(EnvVars::PYTHONUTF8, "1"), @r###"
|
||||
.arg(BUILT_BY_UV_TEST_SCRIPT), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
@ -138,13 +134,9 @@ fn built_by_uv_direct() -> Result<()> {
|
|||
|
||||
drop(wheel_dir);
|
||||
|
||||
uv_snapshot!(context
|
||||
.run()
|
||||
.arg("python")
|
||||
uv_snapshot!(context.python_command()
|
||||
.arg("-c")
|
||||
.arg(BUILT_BY_UV_TEST_SCRIPT)
|
||||
// Python on windows
|
||||
.env(EnvVars::PYTHONUTF8, "1"), @r###"
|
||||
.arg(BUILT_BY_UV_TEST_SCRIPT), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
@ -169,7 +161,8 @@ fn built_by_uv_editable() -> Result<()> {
|
|||
|
||||
// Without the editable, pytest fails.
|
||||
context.pip_install().arg("pytest").assert().success();
|
||||
Command::new(context.interpreter())
|
||||
context
|
||||
.python_command()
|
||||
.arg("-m")
|
||||
.arg("pytest")
|
||||
.current_dir(built_by_uv)
|
||||
|
@ -200,7 +193,7 @@ fn built_by_uv_editable() -> Result<()> {
|
|||
drop(wheel_dir);
|
||||
|
||||
// Now, pytest passes.
|
||||
uv_snapshot!(Command::new(context.interpreter())
|
||||
uv_snapshot!(context.python_command()
|
||||
.arg("-m")
|
||||
.arg("pytest")
|
||||
// Avoid showing absolute paths and column dependent layout
|
||||
|
@ -340,11 +333,9 @@ fn rename_module() -> Result<()> {
|
|||
.success();
|
||||
|
||||
// Importing the module with the `module-name` name succeeds.
|
||||
uv_snapshot!(Command::new(context.interpreter())
|
||||
uv_snapshot!(context.python_command()
|
||||
.arg("-c")
|
||||
.arg("import bar")
|
||||
// Python on windows
|
||||
.env(EnvVars::PYTHONUTF8, "1"), @r###"
|
||||
.arg("import bar"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
@ -354,11 +345,9 @@ fn rename_module() -> Result<()> {
|
|||
"###);
|
||||
|
||||
// Importing the package name fails, it was overridden by `module-name`.
|
||||
uv_snapshot!(Command::new(context.interpreter())
|
||||
uv_snapshot!(context.python_command()
|
||||
.arg("-c")
|
||||
.arg("import foo")
|
||||
// Python on windows
|
||||
.env(EnvVars::PYTHONUTF8, "1"), @r###"
|
||||
.arg("import foo"), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
@ -419,11 +408,9 @@ fn rename_module_editable_build() -> Result<()> {
|
|||
.success();
|
||||
|
||||
// Importing the module with the `module-name` name succeeds.
|
||||
uv_snapshot!(Command::new(context.interpreter())
|
||||
uv_snapshot!(context.python_command()
|
||||
.arg("-c")
|
||||
.arg("import bar")
|
||||
// Python on windows
|
||||
.env(EnvVars::PYTHONUTF8, "1"), @r###"
|
||||
.arg("import bar"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
@ -514,11 +501,9 @@ fn build_module_name_normalization() -> Result<()> {
|
|||
.assert()
|
||||
.success();
|
||||
|
||||
uv_snapshot!(Command::new(context.interpreter())
|
||||
uv_snapshot!(context.python_command()
|
||||
.arg("-c")
|
||||
.arg("import Django_plugin")
|
||||
// Python on windows
|
||||
.env(EnvVars::PYTHONUTF8, "1"), @r"
|
||||
.arg("import Django_plugin"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
@ -728,7 +713,7 @@ fn complex_namespace_packages() -> Result<()> {
|
|||
"
|
||||
);
|
||||
|
||||
uv_snapshot!(Command::new(context.interpreter())
|
||||
uv_snapshot!(context.python_command()
|
||||
.arg("-c")
|
||||
.arg("from complex_project.part_b import two; print(two())"),
|
||||
@r"
|
||||
|
@ -769,7 +754,7 @@ fn complex_namespace_packages() -> Result<()> {
|
|||
"
|
||||
);
|
||||
|
||||
uv_snapshot!(Command::new(context.interpreter())
|
||||
uv_snapshot!(context.python_command()
|
||||
.arg("-c")
|
||||
.arg("from complex_project.part_b import two; print(two())"),
|
||||
@r"
|
||||
|
|
|
@ -1085,15 +1085,30 @@ impl TestContext {
|
|||
}
|
||||
|
||||
pub fn interpreter(&self) -> PathBuf {
|
||||
venv_to_interpreter(&self.venv)
|
||||
let venv = &self.venv;
|
||||
if cfg!(unix) {
|
||||
venv.join("bin").join("python")
|
||||
} else if cfg!(windows) {
|
||||
venv.join("Scripts").join("python.exe")
|
||||
} else {
|
||||
unimplemented!("Only Windows and Unix are supported")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn python_command(&self) -> Command {
|
||||
let mut command = self.new_command_with(&self.interpreter());
|
||||
command
|
||||
// Our tests change files in <1s, so we must disable CPython bytecode caching or we'll get stale files
|
||||
// https://github.com/python/cpython/issues/75953
|
||||
.arg("-B")
|
||||
// Python on windows
|
||||
.env(EnvVars::PYTHONUTF8, "1");
|
||||
command
|
||||
}
|
||||
|
||||
/// Run the given python code and check whether it succeeds.
|
||||
pub fn assert_command(&self, command: &str) -> Assert {
|
||||
self.new_command_with(&venv_to_interpreter(&self.venv))
|
||||
// Our tests change files in <1s, so we must disable CPython bytecode caching or we'll get stale files
|
||||
// https://github.com/python/cpython/issues/75953
|
||||
.arg("-B")
|
||||
self.python_command()
|
||||
.arg("-c")
|
||||
.arg(command)
|
||||
.current_dir(&self.temp_dir)
|
||||
|
@ -1102,10 +1117,7 @@ impl TestContext {
|
|||
|
||||
/// Run the given python file and check whether it succeeds.
|
||||
pub fn assert_file(&self, file: impl AsRef<Path>) -> Assert {
|
||||
self.new_command_with(&venv_to_interpreter(&self.venv))
|
||||
// Our tests change files in <1s, so we must disable CPython bytecode caching or we'll get stale files
|
||||
// https://github.com/python/cpython/issues/75953
|
||||
.arg("-B")
|
||||
self.python_command()
|
||||
.arg(file.as_ref())
|
||||
.current_dir(&self.temp_dir)
|
||||
.assert()
|
||||
|
@ -1120,6 +1132,12 @@ impl TestContext {
|
|||
.stdout(version);
|
||||
}
|
||||
|
||||
/// Assert a package is not installed.
|
||||
pub fn assert_not_installed(&self, package: &'static str) {
|
||||
self.assert_command(format!("import {package}").as_str())
|
||||
.failure();
|
||||
}
|
||||
|
||||
/// Generate various escaped regex patterns for the given path.
|
||||
pub fn path_patterns(path: impl AsRef<Path>) -> Vec<String> {
|
||||
let mut patterns = Vec::new();
|
||||
|
@ -1347,16 +1365,6 @@ pub fn venv_bin_path(venv: impl AsRef<Path>) -> PathBuf {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn venv_to_interpreter(venv: &Path) -> PathBuf {
|
||||
if cfg!(unix) {
|
||||
venv.join("bin").join("python")
|
||||
} else if cfg!(windows) {
|
||||
venv.join("Scripts").join("python.exe")
|
||||
} else {
|
||||
unimplemented!("Only Windows and Unix are supported")
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the path to the python interpreter for a specific python version.
|
||||
pub fn get_python(version: &PythonVersion) -> PathBuf {
|
||||
ManagedPythonInstallations::from_settings(None)
|
||||
|
|
|
@ -19,7 +19,7 @@ use wiremock::{
|
|||
use crate::common::{self, decode_token};
|
||||
use crate::common::{
|
||||
DEFAULT_PYTHON_VERSION, TestContext, build_vendor_links_url, download_to_disk, get_bin,
|
||||
uv_snapshot, venv_bin_path, venv_to_interpreter,
|
||||
uv_snapshot, venv_bin_path,
|
||||
};
|
||||
use uv_fs::Simplified;
|
||||
use uv_static::EnvVars;
|
||||
|
@ -9083,8 +9083,7 @@ fn build_tag() {
|
|||
);
|
||||
|
||||
// Ensure that we choose the highest build tag (5).
|
||||
uv_snapshot!(Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-B")
|
||||
uv_snapshot!(context.python_command()
|
||||
.arg("-c")
|
||||
.arg("import build_tag; build_tag.main()")
|
||||
.current_dir(&context.temp_dir), @r###"
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,4 @@
|
|||
use std::env::consts::EXE_SUFFIX;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::Result;
|
||||
use assert_cmd::prelude::*;
|
||||
|
@ -11,24 +9,10 @@ use indoc::indoc;
|
|||
use predicates::Predicate;
|
||||
use url::Url;
|
||||
|
||||
use crate::common::{
|
||||
TestContext, download_to_disk, site_packages_path, uv_snapshot, venv_to_interpreter,
|
||||
};
|
||||
use crate::common::{TestContext, download_to_disk, site_packages_path, uv_snapshot};
|
||||
use uv_fs::{Simplified, copy_dir_all};
|
||||
use uv_static::EnvVars;
|
||||
|
||||
fn check_command(venv: &Path, command: &str, temp_dir: &Path) {
|
||||
Command::new(venv_to_interpreter(venv))
|
||||
// Our tests change files in <1s, so we must disable CPython bytecode caching or we'll get stale files
|
||||
// https://github.com/python/cpython/issues/75953
|
||||
.arg("-B")
|
||||
.arg("-c")
|
||||
.arg(command)
|
||||
.current_dir(temp_dir)
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_requirements_txt() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
@ -463,7 +447,13 @@ fn link() -> Result<()> {
|
|||
"###
|
||||
);
|
||||
|
||||
check_command(&context2.venv, "import iniconfig", &context2.temp_dir);
|
||||
context2
|
||||
.python_command()
|
||||
.arg("-c")
|
||||
.arg("import iniconfig")
|
||||
.current_dir(&context2.temp_dir)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -5221,8 +5211,8 @@ fn target_built_distribution() -> Result<()> {
|
|||
context.assert_command("import iniconfig").failure();
|
||||
|
||||
// Ensure that we can import the package by augmenting the `PYTHONPATH`.
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-B")
|
||||
context
|
||||
.python_command()
|
||||
.arg("-c")
|
||||
.arg("import iniconfig")
|
||||
.env(EnvVars::PYTHONPATH, context.temp_dir.child("target").path())
|
||||
|
@ -5326,8 +5316,8 @@ fn target_source_distribution() -> Result<()> {
|
|||
context.assert_command("import iniconfig").failure();
|
||||
|
||||
// Ensure that we can import the package by augmenting the `PYTHONPATH`.
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-B")
|
||||
context
|
||||
.python_command()
|
||||
.arg("-c")
|
||||
.arg("import iniconfig")
|
||||
.env(EnvVars::PYTHONPATH, context.temp_dir.child("target").path())
|
||||
|
@ -5397,8 +5387,8 @@ fn target_no_build_isolation() -> Result<()> {
|
|||
context.assert_command("import wheel").failure();
|
||||
|
||||
// Ensure that we can import the package by augmenting the `PYTHONPATH`.
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-B")
|
||||
context
|
||||
.python_command()
|
||||
.arg("-c")
|
||||
.arg("import wheel")
|
||||
.env(EnvVars::PYTHONPATH, context.temp_dir.child("target").path())
|
||||
|
@ -5474,8 +5464,8 @@ fn prefix() -> Result<()> {
|
|||
context.assert_command("import iniconfig").failure();
|
||||
|
||||
// Ensure that we can import the package by augmenting the `PYTHONPATH`.
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-B")
|
||||
context
|
||||
.python_command()
|
||||
.arg("-c")
|
||||
.arg("import iniconfig")
|
||||
.env(
|
||||
|
|
|
@ -5,7 +5,7 @@ use assert_cmd::prelude::*;
|
|||
use assert_fs::fixture::ChildPath;
|
||||
use assert_fs::prelude::*;
|
||||
|
||||
use crate::common::{TestContext, get_bin, uv_snapshot, venv_to_interpreter};
|
||||
use crate::common::{TestContext, get_bin, uv_snapshot};
|
||||
|
||||
#[test]
|
||||
fn no_arguments() {
|
||||
|
@ -113,12 +113,7 @@ fn uninstall() -> Result<()> {
|
|||
.assert()
|
||||
.success();
|
||||
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-c")
|
||||
.arg("import markupsafe")
|
||||
.current_dir(&context.temp_dir)
|
||||
.assert()
|
||||
.success();
|
||||
context.assert_command("import markupsafe").success();
|
||||
|
||||
uv_snapshot!(context.pip_uninstall()
|
||||
.arg("MarkupSafe"), @r###"
|
||||
|
@ -132,12 +127,7 @@ fn uninstall() -> Result<()> {
|
|||
"###
|
||||
);
|
||||
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-c")
|
||||
.arg("import markupsafe")
|
||||
.current_dir(&context.temp_dir)
|
||||
.assert()
|
||||
.failure();
|
||||
context.assert_command("import markupsafe").failure();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -156,12 +146,7 @@ fn missing_record() -> Result<()> {
|
|||
.assert()
|
||||
.success();
|
||||
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-c")
|
||||
.arg("import markupsafe")
|
||||
.current_dir(&context.temp_dir)
|
||||
.assert()
|
||||
.success();
|
||||
context.assert_command("import markupsafe").success();
|
||||
|
||||
// Delete the RECORD file.
|
||||
let dist_info = context.site_packages().join("MarkupSafe-2.1.3.dist-info");
|
||||
|
@ -202,11 +187,7 @@ fn uninstall_editable_by_name() -> Result<()> {
|
|||
.assert()
|
||||
.success();
|
||||
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-c")
|
||||
.arg("import poetry_editable")
|
||||
.assert()
|
||||
.success();
|
||||
context.assert_command("import poetry_editable").success();
|
||||
|
||||
// Uninstall the editable by name.
|
||||
uv_snapshot!(context.filters(), context.pip_uninstall()
|
||||
|
@ -221,11 +202,7 @@ fn uninstall_editable_by_name() -> Result<()> {
|
|||
"###
|
||||
);
|
||||
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-c")
|
||||
.arg("import poetry_editable")
|
||||
.assert()
|
||||
.failure();
|
||||
context.assert_command("import poetry_editable").failure();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -251,11 +228,7 @@ fn uninstall_by_path() -> Result<()> {
|
|||
.assert()
|
||||
.success();
|
||||
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-c")
|
||||
.arg("import poetry_editable")
|
||||
.assert()
|
||||
.success();
|
||||
context.assert_command("import poetry_editable").success();
|
||||
|
||||
// Uninstall the editable by path.
|
||||
uv_snapshot!(context.filters(), context.pip_uninstall()
|
||||
|
@ -270,11 +243,7 @@ fn uninstall_by_path() -> Result<()> {
|
|||
"###
|
||||
);
|
||||
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-c")
|
||||
.arg("import poetry_editable")
|
||||
.assert()
|
||||
.failure();
|
||||
context.assert_command("import poetry_editable").failure();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -300,11 +269,7 @@ fn uninstall_duplicate_by_path() -> Result<()> {
|
|||
.assert()
|
||||
.success();
|
||||
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-c")
|
||||
.arg("import poetry_editable")
|
||||
.assert()
|
||||
.success();
|
||||
context.assert_command("import poetry_editable").success();
|
||||
|
||||
// Uninstall the editable by both path and name.
|
||||
uv_snapshot!(context.filters(), context.pip_uninstall()
|
||||
|
@ -320,11 +285,7 @@ fn uninstall_duplicate_by_path() -> Result<()> {
|
|||
"###
|
||||
);
|
||||
|
||||
Command::new(venv_to_interpreter(&context.venv))
|
||||
.arg("-c")
|
||||
.arg("import poetry_editable")
|
||||
.assert()
|
||||
.failure();
|
||||
context.assert_command("import poetry_editable").failure();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ use predicates::prelude::predicate;
|
|||
use uv_static::EnvVars;
|
||||
|
||||
use crate::common::{
|
||||
build_vendor_links_url, get_bin, packse_index_url, python_path_with_versions, uv_snapshot,
|
||||
TestContext,
|
||||
TestContext, build_vendor_links_url, get_bin, packse_index_url, python_path_with_versions,
|
||||
uv_snapshot,
|
||||
};
|
||||
|
||||
/// Provision python binaries and return a `pip compile` command with options shared across all scenarios.
|
||||
|
|
|
@ -5,52 +5,20 @@
|
|||
//!
|
||||
#![cfg(all(feature = "python", feature = "pypi", unix))]
|
||||
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
use assert_cmd::assert::Assert;
|
||||
use assert_cmd::prelude::*;
|
||||
|
||||
use uv_static::EnvVars;
|
||||
|
||||
use crate::common::{
|
||||
build_vendor_links_url, get_bin, packse_index_url, uv_snapshot, venv_to_interpreter,
|
||||
TestContext,
|
||||
};
|
||||
|
||||
fn assert_command(venv: &Path, command: &str, temp_dir: &Path) -> Assert {
|
||||
Command::new(venv_to_interpreter(venv))
|
||||
.arg("-c")
|
||||
.arg(command)
|
||||
.current_dir(temp_dir)
|
||||
.assert()
|
||||
}
|
||||
|
||||
fn assert_installed(venv: &Path, package: &'static str, version: &'static str, temp_dir: &Path) {
|
||||
assert_command(
|
||||
venv,
|
||||
format!("import {package} as package; print(package.__version__, end='')").as_str(),
|
||||
temp_dir,
|
||||
)
|
||||
.success()
|
||||
.stdout(version);
|
||||
}
|
||||
|
||||
fn assert_not_installed(venv: &Path, package: &'static str, temp_dir: &Path) {
|
||||
assert_command(venv, format!("import {package}").as_str(), temp_dir).failure();
|
||||
}
|
||||
use crate::common::{TestContext, build_vendor_links_url, packse_index_url, uv_snapshot};
|
||||
|
||||
/// Create a `pip install` command with options shared across all scenarios.
|
||||
fn command(context: &TestContext) -> Command {
|
||||
let mut command = Command::new(get_bin());
|
||||
let mut command = context.pip_install();
|
||||
command
|
||||
.arg("pip")
|
||||
.arg("install")
|
||||
.arg("--index-url")
|
||||
.arg(packse_index_url())
|
||||
.arg("--find-links")
|
||||
.arg(build_vendor_links_url());
|
||||
context.add_shared_options(&mut command, true);
|
||||
command.env_remove(EnvVars::UV_EXCLUDE_NEWER);
|
||||
command
|
||||
}
|
||||
|
@ -93,25 +61,20 @@ fn {{module_name}}() {
|
|||
{{/resolver_options.python_platform}}
|
||||
{{#root.requires}}
|
||||
.arg("{{requirement}}")
|
||||
{{/root.requires}}, @r###"<snapshot>
|
||||
"###);
|
||||
{{/root.requires}}, @r#"<snapshot>
|
||||
"#);
|
||||
|
||||
{{#expected.explanation}}
|
||||
// {{expected.explanation}}
|
||||
{{/expected.explanation}}
|
||||
{{#expected.satisfiable}}
|
||||
{{#expected.packages}}
|
||||
assert_installed(
|
||||
&context.venv,
|
||||
"{{module_name}}",
|
||||
"{{version}}",
|
||||
&context.temp_dir
|
||||
);
|
||||
context.assert_installed("{{module_name}}", "{{version}}");
|
||||
{{/expected.packages}}
|
||||
{{/expected.satisfiable}}
|
||||
{{^expected.satisfiable}}
|
||||
{{#root.requires}}
|
||||
assert_not_installed(&context.venv, "{{module_name}}", &context.temp_dir);
|
||||
context.assert_not_installed("{{module_name}}");
|
||||
{{/root.requires}}
|
||||
{{/expected.satisfiable}}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use insta::assert_snapshot;
|
|||
|
||||
use uv_static::EnvVars;
|
||||
|
||||
use crate::common::{packse_index_url, TestContext, uv_snapshot};
|
||||
use crate::common::{TestContext, packse_index_url, uv_snapshot};
|
||||
|
||||
{{#scenarios}}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue