From 8534eca219b3080b64928c1c01f7779a3ef43208 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 7 Dec 2024 20:37:05 +0000 Subject: [PATCH] feat(net): don't use panic to signal test should fail --- src/net/system.rs | 87 +++++++++++++++++++++++++++++++++++++++-------- tests/net.rs | 14 ++++---- 2 files changed, 80 insertions(+), 21 deletions(-) diff --git a/src/net/system.rs b/src/net/system.rs index b3ca2d5..4a7d38a 100644 --- a/src/net/system.rs +++ b/src/net/system.rs @@ -611,36 +611,95 @@ impl From for Net { } impl Drop for MockNet { + #[cfg_attr(test, mutants::skip)] #[tracing::instrument] fn drop(&mut self) { - let unused = self.plans.take(); - if unused.is_empty() { - tracing::trace!("no unused expected requests"); - return; // all good + // Don't assert during panic to avoid double panic + if std::thread::panicking() { + return; + } + let unused = self.plans.take(); + if !unused.is_empty() { + log_unused_plans(&unused); + assert!( + unused.is_empty(), + "{} expected requests were not made", + unused.len() + ); } - panic_with_unused_plans(unused); } } impl Drop for Net { + #[cfg_attr(test, mutants::skip)] #[tracing::instrument] fn drop(&mut self) { + // Don't assert during panic to avoid double panic + if std::thread::panicking() { + return; + } if let Some(plans) = &self.plans { let unused = plans.try_lock().expect("lock plans").take(); - if unused.is_empty() { - tracing::trace!("no unused expected requests"); - return; // all good + if !unused.is_empty() { + log_unused_plans(&unused); + assert!( + unused.is_empty(), + "{} expected requests were not made", + unused.len() + ); } - panic_with_unused_plans(unused); } } } -fn panic_with_unused_plans(unused: Vec) { - eprintln!("These requests were expected, but not made:"); - for plan in unused { - eprintln!("- {plan}"); +#[cfg_attr(test, mutants::skip)] +fn log_unused_plans(unused: &[Plan]) { + if !unused.is_empty() { + eprintln!( + "Net::drop(): {} expected requests were not made:\n{}", + unused.len(), + unused + .iter() + .map(|p| format!(" - {}", p)) + .collect::>() + .join("\n") + ); + } +} + +impl Net { + /// Assert that all expected requests were made. + /// This will fail the test if there are any unused plans. + #[cfg_attr(test, mutants::skip)] + pub fn assert_no_unused_plans(&self) { + if let Some(plans) = &self.plans { + let unused = plans.try_lock().expect("lock plans").take(); + if !unused.is_empty() { + log_unused_plans(&unused); + assert!( + unused.is_empty(), + "{} expected requests were not made", + unused.len() + ); + } + } + } +} + +impl MockNet { + /// Assert that all expected requests were made. + /// This will fail the test if there are any unused plans. + #[cfg_attr(test, mutants::skip)] + pub fn assert_no_unused_plans(&self) { + let unused = self.plans.take(); + if !unused.is_empty() { + log_unused_plans(&unused); + assert!( + unused.is_empty(), + "{} expected requests were not made", + unused.len() + ); + } } - panic!("There were expected requests that were not made."); } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/tests/net.rs b/tests/net.rs index 867a2f9..95c2982 100644 --- a/tests/net.rs +++ b/tests/net.rs @@ -335,7 +335,7 @@ async fn test_post_by_header_wrong_value() { } #[tokio::test] -#[should_panic] +#[should_panic(expected = "1 expected requests were not made")] async fn test_unused_post_as_net() { //given let mock_net = kxio::net::mock(); @@ -349,18 +349,18 @@ async fn test_unused_post_as_net() { .body("Post OK") .expect("mock"); - let _net = Net::from(mock_net); + let net = Net::from(mock_net); //when // don't send the planned request - // let _response = Net::from(net).send(client.post(url)).await.expect("send"); + // let _response = net.send(client.post(url)).await.expect("send"); //then - // Drop implementation for net should panic + net.assert_no_unused_plans(); } #[tokio::test] -#[should_panic] +#[should_panic(expected = "1 expected requests were not made")] async fn test_unused_post_as_mocknet() { //given let mock_net = kxio::net::mock(); @@ -376,10 +376,10 @@ async fn test_unused_post_as_mocknet() { //when // don't send the planned request - // let _response = Net::from(net).send(client.post(url)).await.expect("send"); + // let _response = mock_net.send(client.post(url)).await.expect("send"); //then - // Drop implementation for mock_net should panic + mock_net.assert_no_unused_plans(); } #[tokio::test]