Files
uv/crates/puffin-resolver/tests/resolver.rs
T
konsti 76a41066ac Filter out incompatible dists (#398)
Filter out source dists and wheels whose `requires-python` from the
simple api is incompatible with the current python version.

This change showed an important problem: When we use a fake python
version for resolving, building source distributions breaks down because
we can only build with versions we actually have.

This change became surprisingly big. The tests now require python 3.7 to
be installed, but changing that would mean an even bigger change.

Fixes #388
2023-11-13 17:14:07 +01:00

480 lines
13 KiB
Rust

#![cfg(feature = "pypi")]
//! Integration tests for the resolver. These tests rely on a live network connection, and hit
//! `PyPI` directly.
use std::future::Future;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::str::FromStr;
use anyhow::Result;
use once_cell::sync::Lazy;
use tempfile::tempdir;
use pep508_rs::{MarkerEnvironment, Requirement, StringVersion};
use platform_host::{Arch, Os, Platform};
use platform_tags::Tags;
use puffin_client::RegistryClientBuilder;
use puffin_interpreter::{InterpreterInfo, Virtualenv};
use puffin_resolver::{Graph, Manifest, PreReleaseMode, ResolutionMode, Resolver};
use puffin_traits::BuildContext;
struct DummyContext {
interpreter_info: InterpreterInfo,
}
impl BuildContext for DummyContext {
fn cache(&self) -> &Path {
panic!("The test should not need to build source distributions")
}
fn interpreter_info(&self) -> &InterpreterInfo {
&self.interpreter_info
}
fn base_python(&self) -> &Path {
panic!("The test should not need to build source distributions")
}
fn resolve<'a>(
&'a self,
_requirements: &'a [Requirement],
) -> Pin<Box<dyn Future<Output = Result<Vec<Requirement>>> + Send + 'a>> {
panic!("The test should not need to build source distributions")
}
fn install<'a>(
&'a self,
_requirements: &'a [Requirement],
_venv: &'a Virtualenv,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
panic!("The test should not need to build source distributions")
}
fn build_source<'a>(
&'a self,
_sdist: &'a Path,
_subdirectory: Option<&'a Path>,
_wheel_dir: &'a Path,
_package_id: &'a str,
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'a>> {
panic!("The test should not need to build source distributions")
}
}
async fn resolve(
manifest: Manifest,
markers: &'static MarkerEnvironment,
tags: &Tags,
) -> Result<Graph> {
let tempdir = tempdir()?;
let client = RegistryClientBuilder::new(tempdir.path()).build();
let build_context = DummyContext {
interpreter_info: InterpreterInfo::artificial(
Platform::current()?,
markers.clone(),
PathBuf::from("/dev/null"),
PathBuf::from("/dev/null"),
),
};
let resolver = Resolver::new(manifest, markers, tags, &client, &build_context);
Ok(resolver.resolve().await?)
}
#[tokio::test]
async fn black() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("black<=23.9.1").unwrap()],
vec![],
vec![],
ResolutionMode::default(),
PreReleaseMode::default(),
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
#[tokio::test]
async fn black_colorama() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("black[colorama]<=23.9.1").unwrap()],
vec![],
vec![],
ResolutionMode::default(),
PreReleaseMode::default(),
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
#[tokio::test]
async fn black_python_310() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("black<=23.9.1").unwrap()],
vec![],
vec![],
ResolutionMode::default(),
PreReleaseMode::default(),
None,
);
let resolution = resolve(manifest, &MARKERS_310, &TAGS_310).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
/// Resolve `black` with a constraint on `mypy-extensions`, to ensure that constraints are
/// respected.
#[tokio::test]
async fn black_mypy_extensions() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("black<=23.9.1").unwrap()],
vec![Requirement::from_str("mypy-extensions<0.4.4").unwrap()],
vec![],
ResolutionMode::default(),
PreReleaseMode::default(),
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
/// Resolve `black` with a constraint on `mypy-extensions[extra]`, to ensure that extras are
/// ignored when resolving constraints.
#[tokio::test]
async fn black_mypy_extensions_extra() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("black<=23.9.1").unwrap()],
vec![Requirement::from_str("mypy-extensions[extra]<0.4.4").unwrap()],
vec![],
ResolutionMode::default(),
PreReleaseMode::default(),
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
/// Resolve `black` with a redundant constraint on `flake8`, to ensure that constraints don't
/// introduce new dependencies.
#[tokio::test]
async fn black_flake8() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("black<=23.9.1").unwrap()],
vec![Requirement::from_str("flake8<1").unwrap()],
vec![],
ResolutionMode::default(),
PreReleaseMode::default(),
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
#[tokio::test]
async fn black_lowest() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("black>21").unwrap()],
vec![],
vec![],
ResolutionMode::Lowest,
PreReleaseMode::default(),
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
#[tokio::test]
async fn black_lowest_direct() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("black>21").unwrap()],
vec![],
vec![],
ResolutionMode::LowestDirect,
PreReleaseMode::default(),
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
#[tokio::test]
async fn black_respect_preference() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("black<=23.9.1").unwrap()],
vec![],
vec![Requirement::from_str("black==23.9.0").unwrap()],
ResolutionMode::default(),
PreReleaseMode::default(),
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
#[tokio::test]
async fn black_ignore_preference() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("black<=23.9.1").unwrap()],
vec![],
vec![Requirement::from_str("black==23.9.2").unwrap()],
ResolutionMode::default(),
PreReleaseMode::default(),
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
#[tokio::test]
async fn black_disallow_prerelease() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("black<=20.0").unwrap()],
vec![],
vec![],
ResolutionMode::default(),
PreReleaseMode::Disallow,
None,
);
let err = resolve(manifest, &MARKERS_311, &TAGS_311)
.await
.unwrap_err();
insta::assert_display_snapshot!(err);
Ok(())
}
#[tokio::test]
async fn black_allow_prerelease_if_necessary() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("black<=20.0").unwrap()],
vec![],
vec![],
ResolutionMode::default(),
PreReleaseMode::IfNecessary,
None,
);
let err = resolve(manifest, &MARKERS_311, &TAGS_311)
.await
.unwrap_err();
insta::assert_display_snapshot!(err);
Ok(())
}
#[tokio::test]
async fn pylint_disallow_prerelease() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("pylint==2.3.0").unwrap()],
vec![],
vec![],
ResolutionMode::default(),
PreReleaseMode::Disallow,
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
#[tokio::test]
async fn pylint_allow_prerelease() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![Requirement::from_str("pylint==2.3.0").unwrap()],
vec![],
vec![],
ResolutionMode::default(),
PreReleaseMode::Allow,
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
#[tokio::test]
async fn pylint_allow_explicit_prerelease_without_marker() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![
Requirement::from_str("pylint==2.3.0").unwrap(),
Requirement::from_str("isort>=5.0.0").unwrap(),
],
vec![],
vec![],
ResolutionMode::default(),
PreReleaseMode::Explicit,
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
#[tokio::test]
async fn pylint_allow_explicit_prerelease_with_marker() -> Result<()> {
colored::control::set_override(false);
let manifest = Manifest::new(
vec![
Requirement::from_str("pylint==2.3.0").unwrap(),
Requirement::from_str("isort>=5.0.0b").unwrap(),
],
vec![],
vec![],
ResolutionMode::default(),
PreReleaseMode::Explicit,
None,
);
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
insta::assert_display_snapshot!(resolution);
Ok(())
}
static MARKERS_311: Lazy<MarkerEnvironment> = Lazy::new(|| {
MarkerEnvironment {
implementation_name: "cpython".to_string(),
implementation_version: StringVersion::from_str("3.11.5").unwrap(),
os_name: "posix".to_string(),
platform_machine: "arm64".to_string(),
platform_python_implementation: "CPython".to_string(),
platform_release: "21.6.0".to_string(),
platform_system: "Darwin".to_string(),
platform_version: "Darwin Kernel Version 21.6.0: Mon Aug 22 20:19:52 PDT 2022; root:xnu-8020.140.49~2/RELEASE_ARM64_T6000".to_string(),
python_full_version: StringVersion::from_str("3.11.5").unwrap(),
python_version: StringVersion::from_str("3.11").unwrap(),
sys_platform: "darwin".to_string(),
}
});
static TAGS_311: Lazy<Tags> = Lazy::new(|| {
Tags::from_env(
&Platform::new(
Os::Macos {
major: 21,
minor: 6,
},
Arch::Aarch64,
),
(3, 11),
)
.unwrap()
});
static MARKERS_310: Lazy<MarkerEnvironment> = Lazy::new(|| {
MarkerEnvironment {
implementation_name: "cpython".to_string(),
implementation_version: StringVersion::from_str("3.10.5").unwrap(),
os_name: "posix".to_string(),
platform_machine: "arm64".to_string(),
platform_python_implementation: "CPython".to_string(),
platform_release: "21.6.0".to_string(),
platform_system: "Darwin".to_string(),
platform_version: "Darwin Kernel Version 21.6.0: Mon Aug 22 20:19:52 PDT 2022; root:xnu-8020.140.49~2/RELEASE_ARM64_T6000".to_string(),
python_full_version: StringVersion::from_str("3.10.5").unwrap(),
python_version: StringVersion::from_str("3.10").unwrap(),
sys_platform: "darwin".to_string(),
}
});
static TAGS_310: Lazy<Tags> = Lazy::new(|| {
Tags::from_env(
&Platform::new(
Os::Macos {
major: 21,
minor: 6,
},
Arch::Aarch64,
),
(3, 10),
)
.unwrap()
});