Skip to content

Commit 807a93b

Browse files
committed
fix: test concurrency race
1 parent 1f377ba commit 807a93b

1 file changed

Lines changed: 104 additions & 0 deletions

File tree

crates/uv/tests/it/sync.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10725,6 +10725,110 @@ fn url_hash_mismatch() -> Result<()> {
1072510725
Ok(())
1072610726
}
1072710727

10728+
#[tokio::test]
10729+
async fn concurrent_url_source_locks_leave_a_usable_cache() -> Result<()> {
10730+
use std::time::Duration;
10731+
10732+
use wiremock::{
10733+
Mock, MockServer, ResponseTemplate,
10734+
matchers::{method, path},
10735+
};
10736+
10737+
let context1 = uv_test::test_context!("3.12");
10738+
let context2 = uv_test::test_context!("3.12");
10739+
let context3 = uv_test::test_context!("3.12");
10740+
10741+
let server = MockServer::start().await;
10742+
let archive_path = context1
10743+
.workspace_root
10744+
.join("test/links/tqdm-999.0.0.tar.gz");
10745+
let archive_bytes = fs_err::read(&archive_path)?;
10746+
let archive_url = format!("{}/files/tqdm-999.0.0.tar.gz", server.uri());
10747+
let shared_cache = context1.temp_dir.child("shared-cache");
10748+
fs_err::create_dir_all(shared_cache.path())?;
10749+
10750+
Mock::given(method("GET"))
10751+
.and(path("/files/tqdm-999.0.0.tar.gz"))
10752+
.respond_with(
10753+
ResponseTemplate::new(200)
10754+
.set_delay(Duration::from_secs(1))
10755+
.set_body_bytes(archive_bytes),
10756+
)
10757+
.mount(&server)
10758+
.await;
10759+
10760+
let write_pyproject = |context: &TestContext| -> Result<()> {
10761+
context
10762+
.temp_dir
10763+
.child("pyproject.toml")
10764+
.write_str(&formatdoc! { r#"
10765+
[project]
10766+
name = "project"
10767+
version = "0.1.0"
10768+
requires-python = ">=3.12"
10769+
dependencies = ["tqdm @ {archive_url}"]
10770+
"#,
10771+
})?;
10772+
Ok(())
10773+
};
10774+
10775+
write_pyproject(&context1)?;
10776+
write_pyproject(&context2)?;
10777+
write_pyproject(&context3)?;
10778+
10779+
let lock_command = |context: &TestContext| {
10780+
let mut command = std::process::Command::new(uv_test::get_bin!());
10781+
command
10782+
.arg("lock")
10783+
.arg("--cache-dir")
10784+
.arg(shared_cache.path())
10785+
.current_dir(context.temp_dir.path());
10786+
context.add_shared_env(&mut command, false);
10787+
command
10788+
};
10789+
10790+
let child1 = lock_command(&context1).spawn()?;
10791+
let child2 = lock_command(&context2).spawn()?;
10792+
10793+
let output1 = child1.wait_with_output()?;
10794+
let output2 = child2.wait_with_output()?;
10795+
10796+
assert!(
10797+
output1.status.success(),
10798+
"first `uv lock` failed\nstdout:\n{}\nstderr:\n{}",
10799+
String::from_utf8_lossy(&output1.stdout),
10800+
String::from_utf8_lossy(&output1.stderr),
10801+
);
10802+
assert!(
10803+
output2.status.success(),
10804+
"second `uv lock` failed\nstdout:\n{}\nstderr:\n{}",
10805+
String::from_utf8_lossy(&output2.stdout),
10806+
String::from_utf8_lossy(&output2.stderr),
10807+
);
10808+
10809+
let archive_requests = server
10810+
.received_requests()
10811+
.await
10812+
.unwrap_or_default()
10813+
.into_iter()
10814+
.filter(|request| request.url.path() == "/files/tqdm-999.0.0.tar.gz")
10815+
.count();
10816+
assert!(
10817+
archive_requests >= 2,
10818+
"expected at least two archive requests, saw {archive_requests}"
10819+
);
10820+
10821+
let offline_output = lock_command(&context3).arg("--offline").output()?;
10822+
assert!(
10823+
offline_output.status.success(),
10824+
"offline `uv lock` failed after concurrent cache writes\nstdout:\n{}\nstderr:\n{}",
10825+
String::from_utf8_lossy(&offline_output.stdout),
10826+
String::from_utf8_lossy(&offline_output.stderr),
10827+
);
10828+
10829+
Ok(())
10830+
}
10831+
1072810832
#[test]
1072910833
fn path_hash_mismatch() -> Result<()> {
1073010834
let context = uv_test::test_context!("3.12");

0 commit comments

Comments
 (0)