Skip to content

Commit

Permalink
Add some more publish timeout tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ehuss committed Feb 14, 2023
1 parent bb54c28 commit 5eef715
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 39 deletions.
2 changes: 1 addition & 1 deletion crates/cargo-test-support/src/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ pub(crate) fn create_index_line(
json.to_string()
}

pub(crate) fn write_to_index(registry_path: &PathBuf, name: &str, line: String, local: bool) {
pub(crate) fn write_to_index(registry_path: &Path, name: &str, line: String, local: bool) {
let file = cargo_util::registry::make_dep_path(name, false);

// Write file/line in the index.
Expand Down
111 changes: 73 additions & 38 deletions crates/cargo-test-support/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::fmt;
use std::fs::{self, File};
use std::io::{BufRead, BufReader, Read, Write};
use std::net::{SocketAddr, TcpListener, TcpStream};
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::thread::{self, JoinHandle};
use tar::{Builder, Header};
use time::format_description::well_known::Rfc3339;
Expand Down Expand Up @@ -98,6 +98,8 @@ pub struct RegistryBuilder {
configure_registry: bool,
/// API responders.
custom_responders: HashMap<&'static str, Box<dyn Send + Fn(&Request, &HttpServer) -> Response>>,
/// If nonzero, the git index update to be delayed by the given number of seconds.
delayed_index_update: usize,
}

pub struct TestRegistry {
Expand Down Expand Up @@ -157,6 +159,7 @@ impl RegistryBuilder {
configure_registry: true,
configure_token: true,
custom_responders: HashMap::new(),
delayed_index_update: 0,
}
}

Expand All @@ -171,6 +174,13 @@ impl RegistryBuilder {
self
}

/// Configures the git index update to be delayed by the given number of seconds.
#[must_use]
pub fn delayed_index_update(mut self, delay: usize) -> Self {
self.delayed_index_update = delay;
self
}

/// Sets whether or not to initialize as an alternative registry.
#[must_use]
pub fn alternative_named(mut self, alt: &str) -> Self {
Expand Down Expand Up @@ -264,6 +274,7 @@ impl RegistryBuilder {
token.clone(),
self.auth_required,
self.custom_responders,
self.delayed_index_update,
);
let index_url = if self.http_index {
server.index_url()
Expand Down Expand Up @@ -589,6 +600,7 @@ pub struct HttpServer {
token: Token,
auth_required: bool,
custom_responders: HashMap<&'static str, Box<dyn Send + Fn(&Request, &HttpServer) -> Response>>,
delayed_index_update: usize,
}

/// A helper struct that collects the arguments for [HttpServer::check_authorized].
Expand All @@ -610,6 +622,7 @@ impl HttpServer {
&'static str,
Box<dyn Send + Fn(&Request, &HttpServer) -> Response>,
>,
delayed_index_update: usize,
) -> HttpServerHandle {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
Expand All @@ -621,6 +634,7 @@ impl HttpServer {
token,
auth_required,
custom_responders: api_responders,
delayed_index_update,
};
let handle = Some(thread::spawn(move || server.start()));
HttpServerHandle { addr, handle }
Expand Down Expand Up @@ -1030,49 +1044,23 @@ impl HttpServer {
return self.unauthorized(req);
}

// Write the `.crate`
let dst = self
.dl_path
.join(&new_crate.name)
.join(&new_crate.vers)
.join("download");
t!(fs::create_dir_all(dst.parent().unwrap()));
t!(fs::write(&dst, file));

let deps = new_crate
.deps
.iter()
.map(|dep| {
let (name, package) = match &dep.explicit_name_in_toml {
Some(explicit) => (explicit.to_string(), Some(dep.name.to_string())),
None => (dep.name.to_string(), None),
};
serde_json::json!({
"name": name,
"req": dep.version_req,
"features": dep.features,
"default_features": true,
"target": dep.target,
"optional": dep.optional,
"kind": dep.kind,
"registry": dep.registry,
"package": package,
})
})
.collect::<Vec<_>>();

let line = create_index_line(
serde_json::json!(new_crate.name),
&new_crate.vers,
deps,
&file_cksum,
new_crate.features,
false,
new_crate.links,
None,
);

write_to_index(&self.registry_path, &new_crate.name, line, false);
if self.delayed_index_update == 0 {
save_new_crate(dst, new_crate, file, file_cksum, &self.registry_path);
} else {
let delayed_index_update = self.delayed_index_update;
let registry_path = self.registry_path.clone();
let file = Vec::from(file);
thread::spawn(move || {
thread::sleep(std::time::Duration::new(delayed_index_update as u64, 0));
save_new_crate(dst, new_crate, &file, file_cksum, &registry_path);
});
}

self.ok(&req)
} else {
Expand All @@ -1085,6 +1073,53 @@ impl HttpServer {
}
}

fn save_new_crate(
dst: PathBuf,
new_crate: crates_io::NewCrate,
file: &[u8],
file_cksum: String,
registry_path: &Path,
) {
// Write the `.crate`
t!(fs::create_dir_all(dst.parent().unwrap()));
t!(fs::write(&dst, file));

let deps = new_crate
.deps
.iter()
.map(|dep| {
let (name, package) = match &dep.explicit_name_in_toml {
Some(explicit) => (explicit.to_string(), Some(dep.name.to_string())),
None => (dep.name.to_string(), None),
};
serde_json::json!({
"name": name,
"req": dep.version_req,
"features": dep.features,
"default_features": true,
"target": dep.target,
"optional": dep.optional,
"kind": dep.kind,
"registry": dep.registry,
"package": package,
})
})
.collect::<Vec<_>>();

let line = create_index_line(
serde_json::json!(new_crate.name),
&new_crate.vers,
deps,
&file_cksum,
new_crate.features,
false,
new_crate.links,
None,
);

write_to_index(registry_path, &new_crate.name, line, false);
}

impl Package {
/// Creates a new package builder.
/// Call `publish()` to finalize and build the package.
Expand Down
124 changes: 124 additions & 0 deletions tests/testsuite/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2770,3 +2770,127 @@ See [..]
)
.run();
}

#[cargo_test]
fn timeout_waiting_for_publish() {
// Publish doesn't happen within the timeout window.
let registry = registry::RegistryBuilder::new()
.http_api()
.delayed_index_update(20)
.build();

let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "delay"
version = "0.0.1"
authors = []
license = "MIT"
description = "foo"
"#,
)
.file("src/lib.rs", "")
.file(
".cargo/config.toml",
r#"
[publish]
timeout = 2
"#,
)
.build();

p.cargo("publish --no-verify -Zpublish-timeout")
.replace_crates_io(registry.index_url())
.masquerade_as_nightly_cargo(&["publish-timeout"])
.with_status(0)
// There may be a variable number of "Updating crates.io index" at the
// end, which is timing-dependent.
.with_stderr_contains(
"\
[UPDATING] crates.io index
[WARNING] manifest has no documentation, [..]
See [..]
[PACKAGING] delay v0.0.1 ([CWD])
[PACKAGED] [..] files, [..] ([..] compressed)
[UPLOADING] delay v0.0.1 ([CWD])
[UPDATING] crates.io index
[WAITING] on `delay` to propagate to crates.io index (ctrl-c to wait asynchronously)
",
)
.with_stderr_contains("warning: timed out waiting for `delay` to be in crates.io index")
.run();
}

#[cargo_test]
fn wait_for_git_publish() {
// Slow publish to an index with a git index.
let registry = registry::RegistryBuilder::new()
.http_api()
.delayed_index_update(5)
.build();

// Publish an earlier version
Package::new("delay", "0.0.1")
.file("src/lib.rs", "")
.publish();

let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "delay"
version = "0.0.2"
authors = []
license = "MIT"
description = "foo"
"#,
)
.file("src/lib.rs", "")
.build();

p.cargo("publish --no-verify")
.replace_crates_io(registry.index_url())
.with_status(0)
.with_stderr_contains(
"\
[UPDATING] crates.io index
[WARNING] manifest has no documentation, [..]
See [..]
[PACKAGING] delay v0.0.2 ([CWD])
[PACKAGED] [..] files, [..] ([..] compressed)
[UPLOADING] delay v0.0.2 ([CWD])
[UPDATING] crates.io index
[WAITING] on `delay` to propagate to crates.io index (ctrl-c to wait asynchronously)
",
)
// The exact number of updates is timing dependent. This just checks
// that at least a few show up.
.with_stderr_contains(
"\
[UPDATING] crates.io index
[UPDATING] crates.io index
[UPDATING] crates.io index
",
)
.run();

let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[dependencies]
delay = "0.0.2"
"#,
)
.file("src/main.rs", "fn main() {}")
.build();

p.cargo("check").with_status(0).run();
}

0 comments on commit 5eef715

Please sign in to comment.