mirror of
https://github.com/tokio-rs/axum.git
synced 2024-11-21 14:46:32 +01:00
Cover clone in fallback with tests
This commit is contained in:
parent
0d4e224cae
commit
e66be09d59
4 changed files with 79 additions and 40 deletions
|
@ -328,3 +328,21 @@ async fn merge_router_with_fallback_into_empty() {
|
|||
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
||||
assert_eq!(res.text().await, "outer");
|
||||
}
|
||||
|
||||
#[crate::test]
|
||||
async fn state_isnt_cloned_too_much_with_fallback() {
|
||||
let state = CountingCloneableState::new();
|
||||
|
||||
let app = Router::new()
|
||||
.fallback(|_: State<CountingCloneableState>| async {})
|
||||
.with_state(state.clone());
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
// ignore clones made during setup
|
||||
state.setup_done();
|
||||
|
||||
client.get("/does-not-exist").await;
|
||||
|
||||
assert_eq!(state.count(), 4);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ use crate::{
|
|||
BoxError, Extension, Json, Router, ServiceExt,
|
||||
};
|
||||
use axum_core::extract::Request;
|
||||
use counting_cloneable_state::CountingCloneableState;
|
||||
use futures_util::stream::StreamExt;
|
||||
use http::{
|
||||
header::{ALLOW, CONTENT_LENGTH, HOST},
|
||||
|
@ -27,7 +28,7 @@ use serde_json::json;
|
|||
use std::{
|
||||
convert::Infallible,
|
||||
future::{ready, IntoFuture, Ready},
|
||||
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
};
|
||||
|
@ -905,54 +906,20 @@ fn test_path_for_nested_route() {
|
|||
|
||||
#[crate::test]
|
||||
async fn state_isnt_cloned_too_much() {
|
||||
static SETUP_DONE: AtomicBool = AtomicBool::new(false);
|
||||
static COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
struct AppState;
|
||||
|
||||
impl Clone for AppState {
|
||||
fn clone(&self) -> Self {
|
||||
#[rustversion::since(1.66)]
|
||||
#[track_caller]
|
||||
fn count() {
|
||||
if SETUP_DONE.load(Ordering::SeqCst) {
|
||||
let bt = std::backtrace::Backtrace::force_capture();
|
||||
let bt = bt
|
||||
.to_string()
|
||||
.lines()
|
||||
.filter(|line| line.contains("axum") || line.contains("./src"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
println!("AppState::Clone:\n===============\n{bt}\n");
|
||||
COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
#[rustversion::not(since(1.66))]
|
||||
fn count() {
|
||||
if SETUP_DONE.load(Ordering::SeqCst) {
|
||||
COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
count();
|
||||
|
||||
Self
|
||||
}
|
||||
}
|
||||
let state = CountingCloneableState::new();
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(|_: State<AppState>| async {}))
|
||||
.with_state(AppState);
|
||||
.route("/", get(|_: State<CountingCloneableState>| async {}))
|
||||
.with_state(state.clone());
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
// ignore clones made during setup
|
||||
SETUP_DONE.store(true, Ordering::SeqCst);
|
||||
state.setup_done();
|
||||
|
||||
client.get("/").await;
|
||||
|
||||
assert_eq!(COUNT.load(Ordering::SeqCst), 3);
|
||||
assert_eq!(state.count(), 3);
|
||||
}
|
||||
|
||||
#[crate::test]
|
||||
|
|
52
axum/src/test_helpers/counting_cloneable_state.rs
Normal file
52
axum/src/test_helpers/counting_cloneable_state.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use std::sync::{
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
pub(crate) struct CountingCloneableState {
|
||||
state: Arc<InnerState>,
|
||||
}
|
||||
|
||||
struct InnerState {
|
||||
setup_done: AtomicBool,
|
||||
count: AtomicUsize,
|
||||
}
|
||||
|
||||
impl CountingCloneableState {
|
||||
pub(crate) fn new() -> Self {
|
||||
let inner_state = InnerState {
|
||||
setup_done: AtomicBool::new(false),
|
||||
count: AtomicUsize::new(0),
|
||||
};
|
||||
CountingCloneableState {
|
||||
state: Arc::new(inner_state),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn setup_done(&self) {
|
||||
self.state.setup_done.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub(crate) fn count(&self) -> usize {
|
||||
self.state.count.load(Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for CountingCloneableState {
|
||||
fn clone(&self) -> Self {
|
||||
let state = self.state.clone();
|
||||
if state.setup_done.load(Ordering::SeqCst) {
|
||||
let bt = std::backtrace::Backtrace::force_capture();
|
||||
let bt = bt
|
||||
.to_string()
|
||||
.lines()
|
||||
.filter(|line| line.contains("axum") || line.contains("./src"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
println!("AppState::Clone:\n===============\n{bt}\n");
|
||||
state.count.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
CountingCloneableState { state }
|
||||
}
|
||||
}
|
|
@ -7,6 +7,8 @@ pub(crate) use self::test_client::*;
|
|||
|
||||
pub(crate) mod tracing_helpers;
|
||||
|
||||
pub(crate) mod counting_cloneable_state;
|
||||
|
||||
pub(crate) fn assert_send<T: Send>() {}
|
||||
pub(crate) fn assert_sync<T: Sync>() {}
|
||||
|
||||
|
|
Loading…
Reference in a new issue