From 4912b0e7b0decad2585e70c6e7791b3140f6c100 Mon Sep 17 00:00:00 2001 From: Leynos Date: Fri, 1 Aug 2025 17:22:14 +0100 Subject: [PATCH 1/6] Refactor helpers using forwarding macros --- wireframe_testing/src/helpers.rs | 399 ++++++++++++++++--------------- 1 file changed, 204 insertions(+), 195 deletions(-) diff --git a/wireframe_testing/src/helpers.rs b/wireframe_testing/src/helpers.rs index 13749250..403af7e5 100644 --- a/wireframe_testing/src/helpers.rs +++ b/wireframe_testing/src/helpers.rs @@ -33,6 +33,29 @@ impl TestSerializer for T where const DEFAULT_CAPACITY: usize = 4096; +macro_rules! forward_default { + ( + $(#[$docs:meta])* $vis:vis fn $name:ident( + $app:ident : $app_ty:ty, + $arg:ident : $arg_ty:ty + ) -> $ret:ty + => $inner:ident($app_expr:ident, $arg_expr:expr) + ) => { + $(#[$docs])* + $vis async fn $name( + $app: $app_ty, + $arg: $arg_ty, + ) -> $ret + where + S: TestSerializer, + C: Send + 'static, + E: Packet, + { + $inner($app_expr, $arg_expr, DEFAULT_CAPACITY).await + } + }; +} + async fn drive_internal( server_fn: F, frames: Vec>, @@ -107,55 +130,85 @@ where drive_with_frame_with_capacity(app, frame, DEFAULT_CAPACITY).await } -/// Drive `app` with a single frame using a duplex buffer of `capacity` bytes. -/// -/// Adjusting the buffer size helps exercise edge cases such as small channels. -/// -/// ```rust -/// # use wireframe_testing::{drive_with_frame_with_capacity, processor}; -/// # use wireframe::app::WireframeApp; -/// # async fn demo() -> tokio::io::Result<()> { -/// let app = WireframeApp::new().frame_processor(processor()).unwrap(); -/// let bytes = drive_with_frame_with_capacity(app, vec![0], 512).await?; -/// # Ok(()) -/// # } -/// ``` -pub async fn drive_with_frame_with_capacity( - app: WireframeApp, - frame: Vec, - capacity: usize, -) -> io::Result> -where - S: TestSerializer, - C: Send + 'static, - E: Packet, -{ - drive_with_frames_with_capacity(app, vec![frame], capacity).await +macro_rules! forward_with_capacity { + ( + $(#[$docs:meta])* $vis:vis fn $name:ident( + $app:ident : $app_ty:ty, + $arg:ident : $arg_ty:ty, + capacity: usize + ) -> $ret:ty + => $inner:ident($app_expr:ident, $arg_expr:expr, capacity) + ) => { + $(#[$docs])* + $vis async fn $name( + $app: $app_ty, + $arg: $arg_ty, + capacity: usize, + ) -> $ret + where + S: TestSerializer, + C: Send + 'static, + E: Packet, + { + $inner($app_expr, $arg_expr, capacity).await + } + }; } -/// Drive `app` with a sequence of frames using the default buffer size. -/// -/// Each frame is written to the duplex stream in order. -/// -/// ```rust -/// # use wireframe_testing::{drive_with_frames, processor}; -/// # use wireframe::app::WireframeApp; -/// # async fn demo() -> tokio::io::Result<()> { -/// let app = WireframeApp::new().frame_processor(processor()).unwrap(); -/// let out = drive_with_frames(app, vec![vec![1], vec![2]]).await?; -/// # Ok(()) -/// # } -/// ``` -pub async fn drive_with_frames( - app: WireframeApp, - frames: Vec>, -) -> io::Result> -where - S: TestSerializer, - C: Send + 'static, - E: Packet, -{ - drive_with_frames_with_capacity(app, frames, DEFAULT_CAPACITY).await +forward_default! { + /// Drive `app` with a single length-prefixed `frame` and return the bytes + /// produced by the server. + /// + /// The app runs on an in-memory duplex stream so tests need not open real + /// sockets. + /// + /// ```rust + /// # use wireframe_testing::{drive_with_frame, processor}; + /// # use wireframe::app::WireframeApp; + /// # async fn demo() -> tokio::io::Result<()> { + /// let app = WireframeApp::new().frame_processor(processor()).unwrap(); + /// let bytes = drive_with_frame(app, vec![1, 2, 3]).await?; + /// # Ok(()) + /// # } + /// ``` + pub fn drive_with_frame(app: WireframeApp, frame: Vec) -> io::Result> + => drive_with_frame_with_capacity(app, frame) +} + +forward_with_capacity! { + /// Drive `app` with a single frame using a duplex buffer of `capacity` bytes. + /// + /// Adjusting the buffer size helps exercise edge cases such as small channels. + /// + /// ```rust + /// # use wireframe_testing::{drive_with_frame_with_capacity, processor}; + /// # use wireframe::app::WireframeApp; + /// # async fn demo() -> tokio::io::Result<()> { + /// let app = WireframeApp::new().frame_processor(processor()).unwrap(); + /// let bytes = drive_with_frame_with_capacity(app, vec![0], 512).await?; + /// # Ok(()) + /// # } + /// ``` + pub fn drive_with_frame_with_capacity(app: WireframeApp, frame: Vec, capacity: usize) -> io::Result> + => drive_with_frames_with_capacity(app, vec![frame], capacity) +} + +forward_default! { + /// Drive `app` with a sequence of frames using the default buffer size. + /// + /// Each frame is written to the duplex stream in order. + /// + /// ```rust + /// # use wireframe_testing::{drive_with_frames, processor}; + /// # use wireframe::app::WireframeApp; + /// # async fn demo() -> tokio::io::Result<()> { + /// let app = WireframeApp::new().frame_processor(processor()).unwrap(); + /// let out = drive_with_frames(app, vec![vec![1], vec![2]]).await?; + /// # Ok(()) + /// # } + /// ``` + pub fn drive_with_frames(app: WireframeApp, frames: Vec>) -> io::Result> + => drive_with_frames_with_capacity(app, frames) } /// Drive `app` with multiple frames using a duplex buffer of `capacity` bytes. @@ -189,75 +242,53 @@ where .await } -/// Feed a single frame into a mutable `app`, allowing the instance to be reused -/// across calls. -/// -/// ```rust -/// # use wireframe_testing::{drive_with_frame_mut, processor}; -/// # use wireframe::app::WireframeApp; -/// # async fn demo() -> tokio::io::Result<()> { -/// let mut app = WireframeApp::new().frame_processor(processor()).unwrap(); -/// let bytes = drive_with_frame_mut(&mut app, vec![1]).await?; -/// # Ok(()) -/// # } -/// ``` -pub async fn drive_with_frame_mut( - app: &mut WireframeApp, - frame: Vec, -) -> io::Result> -where - S: TestSerializer, - C: Send + 'static, - E: Packet, -{ - drive_with_frame_with_capacity_mut(app, frame, DEFAULT_CAPACITY).await +forward_default! { + /// Feed a single frame into a mutable `app`, allowing the instance to be reused + /// across calls. + /// + /// ```rust + /// # use wireframe_testing::{drive_with_frame_mut, processor}; + /// # use wireframe::app::WireframeApp; + /// # async fn demo() -> tokio::io::Result<()> { + /// let mut app = WireframeApp::new().frame_processor(processor()).unwrap(); + /// let bytes = drive_with_frame_mut(&mut app, vec![1]).await?; + /// # Ok(()) + /// # } + /// ``` + pub fn drive_with_frame_mut(app: &mut WireframeApp, frame: Vec) -> io::Result> + => drive_with_frame_with_capacity_mut(app, frame) } -/// Feed a single frame into `app` using a duplex buffer of `capacity` bytes. -/// -/// ```rust -/// # use wireframe_testing::{drive_with_frame_with_capacity_mut, processor}; -/// # use wireframe::app::WireframeApp; -/// # async fn demo() -> tokio::io::Result<()> { -/// let mut app = WireframeApp::new().frame_processor(processor()).unwrap(); -/// let bytes = drive_with_frame_with_capacity_mut(&mut app, vec![1], 256).await?; -/// # Ok(()) -/// # } -/// ``` -pub async fn drive_with_frame_with_capacity_mut( - app: &mut WireframeApp, - frame: Vec, - capacity: usize, -) -> io::Result> -where - S: TestSerializer, - C: Send + 'static, - E: Packet, -{ - drive_with_frames_with_capacity_mut(app, vec![frame], capacity).await +forward_with_capacity! { + /// Feed a single frame into `app` using a duplex buffer of `capacity` bytes. + /// + /// ```rust + /// # use wireframe_testing::{drive_with_frame_with_capacity_mut, processor}; + /// # use wireframe::app::WireframeApp; + /// # async fn demo() -> tokio::io::Result<()> { + /// let mut app = WireframeApp::new().frame_processor(processor()).unwrap(); + /// let bytes = drive_with_frame_with_capacity_mut(&mut app, vec![1], 256).await?; + /// # Ok(()) + /// # } + /// ``` + pub fn drive_with_frame_with_capacity_mut(app: &mut WireframeApp, frame: Vec, capacity: usize) -> io::Result> + => drive_with_frames_with_capacity_mut(app, vec![frame], capacity) } -/// Feed multiple frames into a mutable `app`. -/// -/// ```rust -/// # use wireframe_testing::{drive_with_frames_mut, processor}; -/// # use wireframe::app::WireframeApp; -/// # async fn demo() -> tokio::io::Result<()> { -/// let mut app = WireframeApp::new().frame_processor(processor()).unwrap(); -/// let out = drive_with_frames_mut(&mut app, vec![vec![1], vec![2]]).await?; -/// # Ok(()) -/// # } -/// ``` -pub async fn drive_with_frames_mut( - app: &mut WireframeApp, - frames: Vec>, -) -> io::Result> -where - S: TestSerializer, - C: Send + 'static, - E: Packet, -{ - drive_with_frames_with_capacity_mut(app, frames, DEFAULT_CAPACITY).await +forward_default! { + /// Feed multiple frames into a mutable `app`. + /// + /// ```rust + /// # use wireframe_testing::{drive_with_frames_mut, processor}; + /// # use wireframe::app::WireframeApp; + /// # async fn demo() -> tokio::io::Result<()> { + /// let mut app = WireframeApp::new().frame_processor(processor()).unwrap(); + /// let out = drive_with_frames_mut(&mut app, vec![vec![1], vec![2]]).await?; + /// # Ok(()) + /// # } + /// ``` + pub fn drive_with_frames_mut(app: &mut WireframeApp, frames: Vec>) -> io::Result> + => drive_with_frames_with_capacity_mut(app, frames) } /// Feed multiple frames into `app` with a duplex buffer of `capacity` bytes. @@ -323,93 +354,71 @@ where drive_with_frame(app, framed.to_vec()).await } -/// Run `app` with a single input `frame` using the default buffer capacity. -/// -/// # Errors -/// -/// Returns any I/O errors encountered while interacting with the in-memory -/// duplex stream. -/// -/// ```rust -/// # use wireframe_testing::{run_app_with_frame, processor}; -/// # use wireframe::app::WireframeApp; -/// # async fn demo() -> tokio::io::Result<()> { -/// let app = WireframeApp::new().frame_processor(processor()).unwrap(); -/// let out = run_app_with_frame(app, vec![1]).await?; -/// # Ok(()) -/// # } -/// ``` -pub async fn run_app_with_frame( - app: WireframeApp, - frame: Vec, -) -> io::Result> -where - S: TestSerializer, - C: Send + 'static, - E: Packet, -{ - run_app_with_frame_with_capacity(app, frame, DEFAULT_CAPACITY).await +forward_default! { + /// Run `app` with a single input `frame` using the default buffer capacity. + /// + /// # Errors + /// + /// Returns any I/O errors encountered while interacting with the in-memory + /// duplex stream. + /// + /// ```rust + /// # use wireframe_testing::{run_app_with_frame, processor}; + /// # use wireframe::app::WireframeApp; + /// # async fn demo() -> tokio::io::Result<()> { + /// let app = WireframeApp::new().frame_processor(processor()).unwrap(); + /// let out = run_app_with_frame(app, vec![1]).await?; + /// # Ok(()) + /// # } + /// ``` + pub fn run_app_with_frame(app: WireframeApp, frame: Vec) -> io::Result> + => run_app_with_frame_with_capacity(app, frame) } -/// Drive `app` with a single frame using a duplex buffer of `capacity` bytes. -/// -/// # Errors -/// -/// Propagates any I/O errors from the in-memory connection. -/// -/// # Panics -/// -/// Panics if the spawned task running the application panics. -/// -/// ```rust -/// # use wireframe_testing::{run_app_with_frame_with_capacity, processor}; -/// # use wireframe::app::WireframeApp; -/// # async fn demo() -> tokio::io::Result<()> { -/// let app = WireframeApp::new().frame_processor(processor()).unwrap(); -/// let out = run_app_with_frame_with_capacity(app, vec![1], 128).await?; -/// # Ok(()) -/// # } -/// ``` -pub async fn run_app_with_frame_with_capacity( - app: WireframeApp, - frame: Vec, - capacity: usize, -) -> io::Result> -where - S: TestSerializer, - C: Send + 'static, - E: Packet, -{ - run_app_with_frames_with_capacity(app, vec![frame], capacity).await +forward_with_capacity! { + /// Drive `app` with a single frame using a duplex buffer of `capacity` bytes. + /// + /// # Errors + /// + /// Propagates any I/O errors from the in-memory connection. + /// + /// # Panics + /// + /// Panics if the spawned task running the application panics. + /// + /// ```rust + /// # use wireframe_testing::{run_app_with_frame_with_capacity, processor}; + /// # use wireframe::app::WireframeApp; + /// # async fn demo() -> tokio::io::Result<()> { + /// let app = WireframeApp::new().frame_processor(processor()).unwrap(); + /// let out = run_app_with_frame_with_capacity(app, vec![1], 128).await?; + /// # Ok(()) + /// # } + /// ``` + pub fn run_app_with_frame_with_capacity(app: WireframeApp, frame: Vec, capacity: usize) -> io::Result> + => run_app_with_frames_with_capacity(app, vec![frame], capacity) } -/// Run `app` with multiple input `frames` using the default buffer capacity. -/// -/// # Errors -/// -/// Returns any I/O errors encountered while interacting with the in-memory -/// duplex stream. -/// -/// ```rust -/// # use wireframe_testing::{run_app_with_frames, processor}; -/// # use wireframe::app::WireframeApp; -/// # async fn demo() -> tokio::io::Result<()> { -/// let app = WireframeApp::new().frame_processor(processor()).unwrap(); -/// let out = run_app_with_frames(app, vec![vec![1], vec![2]]).await?; -/// # Ok(()) -/// # } -/// ``` -#[allow(dead_code)] -pub async fn run_app_with_frames( - app: WireframeApp, - frames: Vec>, -) -> io::Result> -where - S: TestSerializer, - C: Send + 'static, - E: Packet, -{ - run_app_with_frames_with_capacity(app, frames, DEFAULT_CAPACITY).await +forward_default! { + #[allow(dead_code)] + /// Run `app` with multiple input `frames` using the default buffer capacity. + /// + /// # Errors + /// + /// Returns any I/O errors encountered while interacting with the in-memory + /// duplex stream. + /// + /// ```rust + /// # use wireframe_testing::{run_app_with_frames, processor}; + /// # use wireframe::app::WireframeApp; + /// # async fn demo() -> tokio::io::Result<()> { + /// let app = WireframeApp::new().frame_processor(processor()).unwrap(); + /// let out = run_app_with_frames(app, vec![vec![1], vec![2]]).await?; + /// # Ok(()) + /// # } + /// ``` + pub fn run_app_with_frames(app: WireframeApp, frames: Vec>) -> io::Result> + => run_app_with_frames_with_capacity(app, frames) } /// Drive `app` with multiple frames using a duplex buffer of `capacity` bytes. From 639d155a4303c5aa1b65e5f8944c6559a7b75b9f Mon Sep 17 00:00:00 2001 From: Payton McIntosh Date: Sun, 3 Aug 2025 22:55:10 +0100 Subject: [PATCH 2/6] Fix lint issues --- Cargo.lock | 1 + wireframe_testing/src/helpers.rs | 26 -------------------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f042e08e..2ddc882f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2842,6 +2842,7 @@ version = "0.1.0" dependencies = [ "bincode", "bytes", + "futures", "log", "logtest", "metrics-util", diff --git a/wireframe_testing/src/helpers.rs b/wireframe_testing/src/helpers.rs index 403af7e5..7d3cb183 100644 --- a/wireframe_testing/src/helpers.rs +++ b/wireframe_testing/src/helpers.rs @@ -103,32 +103,6 @@ where Ok(buf) } -/// Drive `app` with a single length-prefixed `frame` and return the bytes -/// produced by the server. -/// -/// The app runs on an in-memory duplex stream so tests need not open real -/// sockets. -/// -/// ```rust -/// # use wireframe_testing::{drive_with_frame, processor}; -/// # use wireframe::app::WireframeApp; -/// # async fn demo() -> tokio::io::Result<()> { -/// let app = WireframeApp::new().frame_processor(processor()).unwrap(); -/// let bytes = drive_with_frame(app, vec![1, 2, 3]).await?; -/// # Ok(()) -/// # } -/// ``` -pub async fn drive_with_frame( - app: WireframeApp, - frame: Vec, -) -> io::Result> -where - S: TestSerializer, - C: Send + 'static, - E: Packet, -{ - drive_with_frame_with_capacity(app, frame, DEFAULT_CAPACITY).await -} macro_rules! forward_with_capacity { ( From 2d5ec0439dca75e990e6dd994b37f78041425f76 Mon Sep 17 00:00:00 2001 From: Payton McIntosh Date: Sun, 3 Aug 2025 22:57:21 +0100 Subject: [PATCH 3/6] Enable use of Crush --- .gitignore | 1 + CRUSH.md | 1 + 2 files changed, 2 insertions(+) create mode 120000 CRUSH.md diff --git a/.gitignore b/.gitignore index 324c57f7..221eb3b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target/ **/*.rs.bk +.crush diff --git a/CRUSH.md b/CRUSH.md new file mode 120000 index 00000000..47dc3e3d --- /dev/null +++ b/CRUSH.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file From e51356ef10c1e7ade871fb8e1f40722d6545460c Mon Sep 17 00:00:00 2001 From: Payton McIntosh Date: Sun, 3 Aug 2025 23:00:29 +0100 Subject: [PATCH 4/6] Restore missing blank line --- AGENTS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AGENTS.md b/AGENTS.md index 06f65d12..207724a7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,6 +30,7 @@ the test logic. - **Keep file size managable.** No single code file may be longer than 400 lines. + Long switch statements or dispatch tables should be broken up by feature and constituents colocated with targets. Large blocks of test data should be moved to external data files. From 6d113a06d6224d5be0626b00d6117419e28157c8 Mon Sep 17 00:00:00 2001 From: Payton McIntosh Date: Sun, 3 Aug 2025 23:00:54 +0100 Subject: [PATCH 5/6] Update locking --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 2ddc882f..daf3fbc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2832,6 +2832,7 @@ dependencies = [ "tokio", "tokio-util", "tracing", + "tracing-subscriber", "tracing-test", "wireframe_testing", ] From d3de319bf2cae2c1de657b668162290f0f54bff9 Mon Sep 17 00:00:00 2001 From: Payton McIntosh Date: Sun, 3 Aug 2025 23:01:07 +0100 Subject: [PATCH 6/6] Apply formatting --- wireframe_testing/src/helpers.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/wireframe_testing/src/helpers.rs b/wireframe_testing/src/helpers.rs index 7d3cb183..a31f2316 100644 --- a/wireframe_testing/src/helpers.rs +++ b/wireframe_testing/src/helpers.rs @@ -103,7 +103,6 @@ where Ok(buf) } - macro_rules! forward_with_capacity { ( $(#[$docs:meta])* $vis:vis fn $name:ident(