diff --git a/Cargo.lock b/Cargo.lock index 1578b01..9c4c3ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -719,6 +719,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "irtt-integration-tests" +version = "0.0.0" +dependencies = [ + "irtt-client", + "irtt-proto", + "irtt-server", + "tokio", +] + [[package]] name = "irtt-proto" version = "0.5.0" @@ -734,7 +744,6 @@ name = "irtt-server" version = "0.5.0" dependencies = [ "getrandom 0.4.3", - "irtt-client", "irtt-proto", "nix 0.31.3", "socket2", diff --git a/Cargo.toml b/Cargo.toml index d2407dc..c2d8201 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "crates/irtt-server", "crates/irtt-stats", "crates/irtt-cli", + "crates/irtt-integration-tests", ] resolver = "2" diff --git a/crates/irtt-client/src/async_client/in_tree_server_support.rs b/crates/irtt-client/src/async_client/in_tree_server_support.rs new file mode 100644 index 0000000..0375aa3 --- /dev/null +++ b/crates/irtt-client/src/async_client/in_tree_server_support.rs @@ -0,0 +1,82 @@ +//! A real `irtt-server` running in-process, for tests that need to prove +//! `AsyncClient` and the blocking `Client` are equivalent against a real peer. +//! +//! This duplicates `crates/irtt-integration-tests/tests/support/in_tree_server.rs` +//! rather than depending on it: this module backs private (`super::*`) unit +//! tests here, while the integration crate covers black-box client/server +//! behavior, and neither can depend on the other's test-only code. + +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + sync::mpsc, + thread::{self, JoinHandle}, + time::Duration, +}; + +use irtt_server::{Server, ServerConfig}; +use tokio::sync::oneshot; + +const STARTUP_TIMEOUT: Duration = Duration::from_secs(2); + +pub struct InTreeServer { + pub addr: SocketAddr, + shutdown: Option>, + done: Option>>, +} + +impl InTreeServer { + pub fn start(config: ServerConfig) -> Self { + let (startup_tx, startup_rx) = mpsc::sync_channel(1); + let (shutdown_tx, shutdown_rx) = oneshot::channel(); + let done = thread::spawn(move || { + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("failed to build in-tree test server runtime"); + runtime.block_on(async move { + let bind_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0); + let mut server = Server::bind(bind_addr, config).await?; + startup_tx + .send(server.local_addr()?) + .expect("test stopped waiting for in-tree server startup"); + server + .run(async { + let _ = shutdown_rx.await; + }) + .await + }) + }); + + let addr = match startup_rx.recv_timeout(STARTUP_TIMEOUT) { + Ok(addr) => addr, + Err(error) => { + let _ = shutdown_tx.send(()); + let result = done.join().expect("in-tree test server thread panicked"); + panic!("in-tree test server failed before startup ({error}): {result:?}"); + } + }; + + Self { + addr, + shutdown: Some(shutdown_tx), + done: Some(done), + } + } + + fn stop_inner(&mut self) { + if let Some(shutdown) = self.shutdown.take() { + let _ = shutdown.send(()); + } + if let Some(done) = self.done.take() { + done.join() + .expect("in-tree test server thread panicked") + .expect("in-tree test server stopped with an error"); + } + } +} + +impl Drop for InTreeServer { + fn drop(&mut self) { + self.stop_inner(); + } +} diff --git a/crates/irtt-client/src/async_client/tests.rs b/crates/irtt-client/src/async_client/tests.rs index 9e1278b..eb02a86 100644 --- a/crates/irtt-client/src/async_client/tests.rs +++ b/crates/irtt-client/src/async_client/tests.rs @@ -14,7 +14,7 @@ use irtt_proto::{ }; use tokio::runtime::{Builder, Runtime}; -#[path = "../../tests/support/in_tree_server.rs"] +#[path = "in_tree_server_support.rs"] mod in_tree_server; use super::*; diff --git a/crates/irtt-integration-tests/Cargo.toml b/crates/irtt-integration-tests/Cargo.toml new file mode 100644 index 0000000..198dc7f --- /dev/null +++ b/crates/irtt-integration-tests/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "irtt-integration-tests" +version = "0.0.0" +edition.workspace = true +rust-version.workspace = true +publish = false + +[dependencies] +irtt-client = { workspace = true, features = ["ancillary", "tokio"] } +irtt-server.workspace = true +irtt-proto.workspace = true +tokio = { version = "1.53.1", default-features = false, features = ["rt", "net", "time", "sync", "macros"] } diff --git a/crates/irtt-server/tests/async_client.rs b/crates/irtt-integration-tests/tests/async_client.rs similarity index 100% rename from crates/irtt-server/tests/async_client.rs rename to crates/irtt-integration-tests/tests/async_client.rs diff --git a/crates/irtt-client/tests/server_fill.rs b/crates/irtt-integration-tests/tests/fill_negotiation.rs similarity index 100% rename from crates/irtt-client/tests/server_fill.rs rename to crates/irtt-integration-tests/tests/fill_negotiation.rs diff --git a/crates/irtt-server/tests/server_fill.rs b/crates/irtt-integration-tests/tests/fill_wire_bytes.rs similarity index 100% rename from crates/irtt-server/tests/server_fill.rs rename to crates/irtt-integration-tests/tests/fill_wire_bytes.rs diff --git a/crates/irtt-client/tests/hmac.rs b/crates/irtt-integration-tests/tests/hmac.rs similarity index 100% rename from crates/irtt-client/tests/hmac.rs rename to crates/irtt-integration-tests/tests/hmac.rs diff --git a/crates/irtt-client/tests/large_packets.rs b/crates/irtt-integration-tests/tests/large_packets.rs similarity index 100% rename from crates/irtt-client/tests/large_packets.rs rename to crates/irtt-integration-tests/tests/large_packets.rs diff --git a/crates/irtt-server/tests/negotiation_policy.rs b/crates/irtt-integration-tests/tests/negotiation_policy.rs similarity index 100% rename from crates/irtt-server/tests/negotiation_policy.rs rename to crates/irtt-integration-tests/tests/negotiation_policy.rs diff --git a/crates/irtt-client/tests/protocol_options.rs b/crates/irtt-integration-tests/tests/protocol_options.rs similarity index 100% rename from crates/irtt-client/tests/protocol_options.rs rename to crates/irtt-integration-tests/tests/protocol_options.rs diff --git a/crates/irtt-client/tests/protocol_options/backend_smoke.rs b/crates/irtt-integration-tests/tests/protocol_options/backend_smoke.rs similarity index 100% rename from crates/irtt-client/tests/protocol_options/backend_smoke.rs rename to crates/irtt-integration-tests/tests/protocol_options/backend_smoke.rs diff --git a/crates/irtt-client/tests/support/in_tree_server.rs b/crates/irtt-integration-tests/tests/support/in_tree_server.rs similarity index 100% rename from crates/irtt-client/tests/support/in_tree_server.rs rename to crates/irtt-integration-tests/tests/support/in_tree_server.rs diff --git a/crates/irtt-client/tests/support/mod.rs b/crates/irtt-integration-tests/tests/support/mod.rs similarity index 100% rename from crates/irtt-client/tests/support/mod.rs rename to crates/irtt-integration-tests/tests/support/mod.rs diff --git a/crates/irtt-client/tests/support/real_irtt.rs b/crates/irtt-integration-tests/tests/support/real_irtt.rs similarity index 100% rename from crates/irtt-client/tests/support/real_irtt.rs rename to crates/irtt-integration-tests/tests/support/real_irtt.rs diff --git a/crates/irtt-server/tests/traffic_class.rs b/crates/irtt-integration-tests/tests/traffic_class.rs similarity index 100% rename from crates/irtt-server/tests/traffic_class.rs rename to crates/irtt-integration-tests/tests/traffic_class.rs diff --git a/crates/irtt-server/tests/wildcard_source.rs b/crates/irtt-integration-tests/tests/wildcard_source.rs similarity index 100% rename from crates/irtt-server/tests/wildcard_source.rs rename to crates/irtt-integration-tests/tests/wildcard_source.rs diff --git a/crates/irtt-server/Cargo.toml b/crates/irtt-server/Cargo.toml index 0cb7398..0d1027e 100644 --- a/crates/irtt-server/Cargo.toml +++ b/crates/irtt-server/Cargo.toml @@ -23,6 +23,3 @@ tokio = { version = "1.53.1", default-features = false, features = ["rt", "net", # source-selection path here; elsewhere a wildcard bind is refused. [target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))'.dependencies] nix = { version = "0.31.3", default-features = false, features = ["net", "uio"] } - -[dev-dependencies] -irtt-client = { workspace = true, features = ["ancillary", "tokio"] }