Skip to content

Commit

Permalink
Add rust tests for different URLs in forks
Browse files Browse the repository at this point in the history
Rust code that uses the files added in the previous commit, currently failing.
  • Loading branch information
konstin committed Jun 24, 2024
1 parent eea3fef commit a84d724
Showing 1 changed file with 374 additions and 0 deletions.
374 changes: 374 additions & 0 deletions crates/uv/tests/branching_urls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,374 @@
use std::env;
use std::path::PathBuf;
use std::process::Command;

use anyhow::Result;
use insta::assert_snapshot;

use crate::common::{copy_dir_ignore, get_bin, uv_snapshot, TestContext, EXCLUDE_NEWER};

mod common;

fn lock_command(context: &TestContext) -> Command {
let mut command = Command::new(get_bin());
command
.arg("lock")
.arg("--preview")
.arg("--cache-dir")
.arg(context.cache_dir.path())
.arg("--exclude-newer")
.arg(EXCLUDE_NEWER)
.env("VIRTUAL_ENV", context.venv.as_os_str())
.env("UV_NO_WRAP", "1")
.current_dir(&context.temp_dir);

if cfg!(all(windows, debug_assertions)) {
// TODO(konstin): Reduce stack usage in debug mode enough that the tests pass with the
// default windows stack of 1MB
command.env("UV_STACK_SIZE", (4 * 1024 * 1024).to_string());
}

command
}

fn branching_urls_dir() -> PathBuf {
env::current_dir()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.join("scripts")
.join("branching-urls")
}

/// The root package has diverging URLs for disjoint markers:
/// ```toml
/// dependencies = [
/// "iniconfig @ https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl ; python_version >= '3.12'",
/// "iniconfig @ https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl ; python_version < '3.12'",
/// ]
/// ```
#[test]
fn branching_urls_disjoint() -> Result<()> {
let context = TestContext::new("3.12");

let scenario = "a1-root-allowed";
let sources_dir = branching_urls_dir().join(scenario);
let work_dir = context.temp_dir.join(scenario);
copy_dir_ignore(sources_dir, &work_dir)?;

uv_snapshot!(context.filters(), lock_command(&context).current_dir(&work_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
Resolved 3 packages in [TIME]
"###
);

Ok(())
}

/// The root package has diverging URLs, but their markers are not disjoint:
/// ```toml
/// dependencies = [
/// "iniconfig @ https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl ; python_version >= '3.11'",
/// "iniconfig @ https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl ; python_version < '3.12'",
/// ]
/// ```
#[test]
fn branching_urls_overlapping() -> Result<()> {
let context = TestContext::new("3.12");

let scenario = "a2-root-conflict";
let sources_dir = branching_urls_dir().join(scenario);
let work_dir = context.temp_dir.join(scenario);
copy_dir_ignore(sources_dir, &work_dir)?;

uv_snapshot!(context.filters(), lock_command(&context).current_dir(&work_dir), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
error: Requirements contain conflicting URLs for package `iniconfig`:
- https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl
- https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl
"###
);

Ok(())
}

/// The root package has diverging URLs, but transitive dependencies have conflicting URLs.
///
/// Requirements:
/// ```text
/// a -> anyio (allowed forking urls to force a split)
/// a -> b -> b1 -> https://../iniconfig-1.1.1-py3-none-any.whl
/// a -> b -> b2 -> https://../iniconfig-2.0.0-py3-none-any.whl
/// ```
#[test]
fn root_package_splits_but_transitive_conflict() -> Result<()> {
let context = TestContext::new("3.12");

let scenario = "a3-transitive-conflict";
let sources_dir = branching_urls_dir().join(scenario);
let work_dir = context.temp_dir.join(scenario);
copy_dir_ignore(sources_dir, &work_dir)?;

uv_snapshot!(context.filters(), lock_command(&context).current_dir(&work_dir), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
error: Requirements contain conflicting URLs for package `iniconfig` in split `python_version < '3.12'`:
- https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl
- https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl
"###
);

Ok(())
}

/// The root package has diverging URLs, and transitive dependencies through an intermediate
/// package have one URL for each side.
///
/// Requirements:
/// ```text
/// a -> anyio==4.4.0 ; python_version >= '3.12'
/// a -> anyio==4.3.0 ; python_version < '3.12'
/// a -> b -> b1 ; python_version < '3.12' -> https://../iniconfig-1.1.1-py3-none-any.whl
/// a -> b -> b2 ; python_version >= '3.12' -> https://../iniconfig-2.0.0-py3-none-any.whl
/// ```
#[test]
fn root_package_splits_transitive_too() -> Result<()> {
let context = TestContext::new("3.12");

let scenario = "a4-transitive-allowed";
let sources_dir = branching_urls_dir().join(scenario);
let work_dir = context.temp_dir.join(scenario);
copy_dir_ignore(sources_dir, &work_dir)?;

uv_snapshot!(context.filters(), lock_command(&context).current_dir(&work_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
Resolved 10 packages in [TIME]
"###
);

Ok(())
}

/// The root package has diverging URLs on one package, and other dependencies have one URL
/// for each side.
///
/// Requirements:
/// ```
/// a -> anyio==4.4.0 ; python_version >= '3.12'
/// a -> anyio==4.3.0 ; python_version < '3.12'
/// a -> b1 ; python_version < '3.12' -> iniconfig==1.1.1
/// a -> b2 ; python_version >= '3.12' -> iniconfig==2.0.0
/// ```
#[test]
fn root_package_splits_other_dependencies_too() -> Result<()> {
let context = TestContext::new("3.12");

let scenario = "a5-fork-in-root";
let sources_dir = branching_urls_dir().join(scenario);
let work_dir = context.temp_dir.join(scenario);
copy_dir_ignore(sources_dir, &work_dir)?;

// TODO(konsti): This passes once https://github.com/astral-sh/uv/pull/4415 is merged
uv_snapshot!(context.filters(), lock_command(&context).current_dir(&work_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
Resolved 9 packages in [TIME]
"###
);

Ok(())
}

/// Whether the dependency comes from the registry or a direct URL depends on the branch.
///
/// ```toml
/// dependencies = [
/// "iniconfig == 1.1.1 ; python_version < '3.12'",
/// "iniconfig @ https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl ; python_version >= '3.12'",
/// ]
/// ```
#[test]
fn branching_between_registry_and_direct_url() -> Result<()> {
let context = TestContext::new("3.12");

let scenario = "a6-registry-and-direct-url-mixed";
let sources_dir = branching_urls_dir().join(scenario);
let work_dir = context.temp_dir.join(scenario);
copy_dir_ignore(sources_dir, &work_dir)?;

uv_snapshot!(context.filters(), lock_command(&context).current_dir(&work_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
Resolved 3 packages in [TIME]
"###
);

// We have source dist and wheel for the registry, but only the wheel for the direct URL.
assert_snapshot!(fs_err::read_to_string(work_dir.join("uv.lock"))?, @r###"
version = 1
requires-python = ">=3.11, <3.13"
[[distribution]]
name = "a"
version = "0.1.0"
source = "editable+."
sdist = { path = "." }
[[distribution.dependencies]]
name = "iniconfig"
version = "1.1.1"
source = "registry+https://pypi.org/simple"
marker = "python_version < '3.12'"
[[distribution.dependencies]]
name = "iniconfig"
version = "2.0.0"
source = "direct+https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl"
marker = "python_version >= '3.12'"
[[distribution]]
name = "iniconfig"
version = "1.1.1"
source = "registry+https://pypi.org/simple"
sdist = { url = "https://files.pythonhosted.org/packages/23/a2/97899f6bd0e873fed3a7e67ae8d3a08b21799430fb4da15cfedf10d6e2c2/iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32", size = 8104 }
wheels = [{ url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", size = 4990 }]
[[distribution]]
name = "iniconfig"
version = "2.0.0"
source = "direct+https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl"
wheels = [{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" }]
"###);

Ok(())
}

/// The root package has two different direct URLs for disjoint forks, but they are from different sources.
///
/// ```toml
/// dependencies = [
/// "iniconfig @ https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl ; python_version < '3.12'",
/// "iniconfig @ git+https://github.com/pytest-dev/iniconfig@93f5930e668c0d1ddf4597e38dd0dea4e2665e7a ; python_version >= '3.12'",
/// ]
/// ```
#[test]
fn branching_urls_of_different_sources_disjoint() -> Result<()> {
let context = TestContext::new("3.12");

let scenario = "a7-different-source-types-allowed";
let sources_dir = branching_urls_dir().join(scenario);
let work_dir = context.temp_dir.join(scenario);
copy_dir_ignore(sources_dir, &work_dir)?;

uv_snapshot!(context.filters(), lock_command(&context).current_dir(&work_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
Resolved 3 packages in [TIME]
"###
);

// We have source dist and wheel for the registry, but only the wheel for the direct URL.
assert_snapshot!(fs_err::read_to_string(work_dir.join("uv.lock"))?, @r###"
version = 1
requires-python = ">=3.11, <3.13"
[[distribution]]
name = "a"
version = "0.1.0"
source = "editable+."
sdist = { path = "." }
[[distribution.dependencies]]
name = "iniconfig"
version = "1.1.1"
source = "direct+https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl"
marker = "python_version < '3.12'"
[[distribution.dependencies]]
name = "iniconfig"
version = "2.0.0"
source = "git+https://github.com/pytest-dev/iniconfig?rev=93f5930e668c0d1ddf4597e38dd0dea4e2665e7a#93f5930e668c0d1ddf4597e38dd0dea4e2665e7a"
marker = "python_version >= '3.12'"
[[distribution]]
name = "iniconfig"
version = "1.1.1"
source = "direct+https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl"
wheels = [{ url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3" }]
[[distribution]]
name = "iniconfig"
version = "2.0.0"
source = "git+https://github.com/pytest-dev/iniconfig?rev=93f5930e668c0d1ddf4597e38dd0dea4e2665e7a#93f5930e668c0d1ddf4597e38dd0dea4e2665e7a"
sdist = { url = "https://github.com/pytest-dev/iniconfig?rev=93f5930e668c0d1ddf4597e38dd0dea4e2665e7a#93f5930e668c0d1ddf4597e38dd0dea4e2665e7a" }
"###);

Ok(())
}

/// The root package has two different direct URLs from different sources, but they are not
/// disjoint.
///
/// ```toml
/// dependencies = [
/// "iniconfig @ https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl ; python_version < '3.12'",
/// "iniconfig @ git+https://github.com/pytest-dev/iniconfig@93f5930e668c0d1ddf4597e38dd0dea4e2665e7a ; python_version >= '3.12'",
/// ]
/// ```
#[test]
fn branching_urls_of_different_sources_conflict() -> Result<()> {
let context = TestContext::new("3.12");

let scenario = "a8-different-source-types-conflict";
let sources_dir = branching_urls_dir().join(scenario);
let work_dir = context.temp_dir.join(scenario);
copy_dir_ignore(sources_dir, &work_dir)?;

uv_snapshot!(context.filters(), lock_command(&context).current_dir(&work_dir), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
error: Requirements contain conflicting URLs for package `iniconfig`:
- git+https://github.com/pytest-dev/iniconfig@93f5930e668c0d1ddf4597e38dd0dea4e2665e7a
- https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl
"###
);

Ok(())
}

0 comments on commit a84d724

Please sign in to comment.