diff --git a/quic/s2n-quic-core/src/state.rs b/quic/s2n-quic-core/src/state.rs index 9f153c39c8..247f11c307 100644 --- a/quic/s2n-quic-core/src/state.rs +++ b/quic/s2n-quic-core/src/state.rs @@ -105,6 +105,89 @@ macro_rules! __state_event__ { ); )* + #[cfg(test)] + pub fn test_transitions() -> impl ::core::fmt::Debug { + use $crate::state::Error; + use ::core::{fmt, result::Result}; + + let mut all_states = [ + // collect all of the states we've observed + $($( + $( + (stringify!($valid), Self::$valid), + )* + (stringify!($target), Self::$target), + )*)* + ]; + + all_states.sort_unstable_by_key(|v| v.0); + let (sorted, _) = $crate::slice::partition_dedup(&mut all_states); + + const EVENT_LEN: usize = { + let mut len = 0; + $({ + let _ = stringify!($event); + len += 1; + })* + len + }; + + let apply = |state: &Self| { + [$({ + let mut state = state.clone(); + let result = state.$event().map(|_| state); + (stringify!($event), result) + }),*] + }; + + struct Transitions { + states: [(&'static str, T); L], + count: usize, + apply: A, + } + + impl fmt::Debug for Transitions + where + T: fmt::Debug, + A: Fn(&T) -> [(&'static str, Result>); EVENT_LEN], + { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut m = f.debug_map(); + + for (name, state) in self.states.iter().take(self.count) { + let events = (self.apply)(state); + m.entry(&format_args!("{name}"), &Entry(events)); + } + + m.finish() + } + } + + struct Entry([(&'static str, Result>); EVENT_LEN]); + + impl fmt::Debug for Entry + where + T: fmt::Debug + { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut m = f.debug_map(); + + for (event, outcome) in self.0.iter() { + m.entry(&format_args!("{event}"), outcome); + } + + m.finish() + } + } + + let count = sorted.len(); + Transitions { + states: all_states, + count, + apply, + } + } + /// Generates a dot graph of all state transitions pub fn dot() -> impl ::core::fmt::Display { struct Dot; diff --git a/quic/s2n-quic-core/src/state/snapshots/s2n_quic_core__state__tests__snapshots.snap b/quic/s2n-quic-core/src/state/snapshots/s2n_quic_core__state__tests__snapshots.snap index 026ffca4a8..e000f12a90 100644 --- a/quic/s2n-quic-core/src/state/snapshots/s2n_quic_core__state__tests__snapshots.snap +++ b/quic/s2n-quic-core/src/state/snapshots/s2n_quic_core__state__tests__snapshots.snap @@ -1,128 +1,86 @@ --- source: quic/s2n-quic-core/src/state/tests.rs -expression: outcomes +expression: "State::test_transitions()" --- -[ - ( - Init, - "on_left", - Ok( +{ + Init: { + on_left: Ok( Left, ), - ), - ( - Init, - "on_right", - Ok( + on_right: Ok( Right, ), - ), - ( - Left, - "on_left", - Ok( + }, + Left: { + on_left: Ok( LeftLeft, ), - ), - ( - Left, - "on_right", - Ok( + on_right: Ok( LeftRight, ), - ), - ( - Right, - "on_left", - Ok( - RightLeft, - ), - ), - ( - Right, - "on_right", - Ok( - RightRight, - ), - ), - ( - LeftLeft, - "on_left", - Err( + }, + LeftLeft: { + on_left: Err( InvalidTransition { current: LeftLeft, event: "on_left", }, ), - ), - ( - LeftLeft, - "on_right", - Err( + on_right: Err( InvalidTransition { current: LeftLeft, event: "on_right", }, ), - ), - ( - LeftRight, - "on_left", - Err( + }, + LeftRight: { + on_left: Err( InvalidTransition { current: LeftRight, event: "on_left", }, ), - ), - ( - LeftRight, - "on_right", - Err( + on_right: Err( InvalidTransition { current: LeftRight, event: "on_right", }, ), - ), - ( - RightLeft, - "on_left", - Err( + }, + Right: { + on_left: Ok( + RightLeft, + ), + on_right: Ok( + RightRight, + ), + }, + RightLeft: { + on_left: Err( InvalidTransition { current: RightLeft, event: "on_left", }, ), - ), - ( - RightLeft, - "on_right", - Err( + on_right: Err( InvalidTransition { current: RightLeft, event: "on_right", }, ), - ), - ( - RightRight, - "on_left", - Err( + }, + RightRight: { + on_left: Err( InvalidTransition { current: RightRight, event: "on_left", }, ), - ), - ( - RightRight, - "on_right", - Err( + on_right: Err( InvalidTransition { current: RightRight, event: "on_right", }, ), - ), -] + }, +} diff --git a/quic/s2n-quic-core/src/state/tests.rs b/quic/s2n-quic-core/src/state/tests.rs index 593fda5bc4..c33c87ee5b 100644 --- a/quic/s2n-quic-core/src/state/tests.rs +++ b/quic/s2n-quic-core/src/state/tests.rs @@ -34,29 +34,7 @@ impl State { #[test] #[cfg_attr(miri, ignore)] fn snapshots() { - let mut outcomes = vec![]; - let states = [ - State::Init, - State::Left, - State::Right, - State::LeftLeft, - State::LeftRight, - State::RightLeft, - State::RightRight, - ]; - for state in states { - macro_rules! push { - ($event:ident) => { - let mut target = state.clone(); - let result = target.$event().map(|_| target); - outcomes.push((state.clone(), stringify!($event), result)); - }; - } - push!(on_left); - push!(on_right); - } - - assert_debug_snapshot!(outcomes); + assert_debug_snapshot!(State::test_transitions()); } #[test] diff --git a/quic/s2n-quic-core/src/stream/state/recv.rs b/quic/s2n-quic-core/src/stream/state/recv.rs index a459de2647..d2bd574dae 100644 --- a/quic/s2n-quic-core/src/stream/state/recv.rs +++ b/quic/s2n-quic-core/src/stream/state/recv.rs @@ -74,31 +74,7 @@ mod tests { #[test] #[cfg_attr(miri, ignore)] fn snapshots() { - let mut outcomes = vec![]; - let states = [ - Receiver::Recv, - Receiver::SizeKnown, - Receiver::DataRecvd, - Receiver::DataRead, - Receiver::ResetRecvd, - Receiver::ResetRead, - ]; - for state in states { - macro_rules! push { - ($event:ident) => { - let mut target = state.clone(); - let result = target.$event().map(|_| target); - outcomes.push((state.clone(), stringify!($event), result)); - }; - } - push!(on_receive_fin); - push!(on_receive_all_data); - push!(on_app_read_all_data); - push!(on_reset); - push!(on_app_read_reset); - } - - assert_debug_snapshot!(outcomes); + assert_debug_snapshot!(Receiver::test_transitions()); } #[test] diff --git a/quic/s2n-quic-core/src/stream/state/send.rs b/quic/s2n-quic-core/src/stream/state/send.rs index 7558b361b3..85fe8a2af0 100644 --- a/quic/s2n-quic-core/src/stream/state/send.rs +++ b/quic/s2n-quic-core/src/stream/state/send.rs @@ -80,33 +80,7 @@ mod tests { #[test] #[cfg_attr(miri, ignore)] fn snapshots() { - let mut outcomes = vec![]; - let states = [ - Sender::Ready, - Sender::Send, - Sender::DataSent, - Sender::DataRecvd, - Sender::ResetQueued, - Sender::ResetSent, - Sender::ResetRecvd, - ]; - for state in states { - macro_rules! push { - ($event:ident) => { - let mut target = state.clone(); - let result = target.$event().map(|_| target); - outcomes.push((state.clone(), stringify!($event), result)); - }; - } - push!(on_send_stream); - push!(on_send_fin); - push!(on_queue_reset); - push!(on_send_reset); - push!(on_recv_all_acks); - push!(on_recv_reset_ack); - } - - assert_debug_snapshot!(outcomes); + assert_debug_snapshot!(Sender::test_transitions()); } #[test] diff --git a/quic/s2n-quic-core/src/stream/state/snapshots/s2n_quic_core__stream__state__recv__tests__snapshots.snap b/quic/s2n-quic-core/src/stream/state/snapshots/s2n_quic_core__stream__state__recv__tests__snapshots.snap index 01095254c4..eead299528 100644 --- a/quic/s2n-quic-core/src/stream/state/snapshots/s2n_quic_core__stream__state__recv__tests__snapshots.snap +++ b/quic/s2n-quic-core/src/stream/state/snapshots/s2n_quic_core__stream__state__recv__tests__snapshots.snap @@ -1,283 +1,175 @@ --- source: quic/s2n-quic-core/src/stream/state/recv.rs -expression: outcomes +expression: "Receiver::test_transitions()" --- -[ - ( - Recv, - "on_receive_fin", - Ok( - SizeKnown, - ), - ), - ( - Recv, - "on_receive_all_data", - Err( +{ + DataRead: { + on_receive_fin: Err( InvalidTransition { - current: Recv, - event: "on_receive_all_data", - }, - ), - ), - ( - Recv, - "on_app_read_all_data", - Err( - InvalidTransition { - current: Recv, - event: "on_app_read_all_data", + current: DataRead, + event: "on_receive_fin", }, ), - ), - ( - Recv, - "on_reset", - Ok( - ResetRecvd, - ), - ), - ( - Recv, - "on_app_read_reset", - Err( + on_receive_all_data: Err( InvalidTransition { - current: Recv, - event: "on_app_read_reset", + current: DataRead, + event: "on_receive_all_data", }, ), - ), - ( - SizeKnown, - "on_receive_fin", - Err( + on_app_read_all_data: Err( NoOp { - current: SizeKnown, + current: DataRead, }, ), - ), - ( - SizeKnown, - "on_receive_all_data", - Ok( - DataRecvd, - ), - ), - ( - SizeKnown, - "on_app_read_all_data", - Err( + on_reset: Err( InvalidTransition { - current: SizeKnown, - event: "on_app_read_all_data", + current: DataRead, + event: "on_reset", }, ), - ), - ( - SizeKnown, - "on_reset", - Ok( - ResetRecvd, - ), - ), - ( - SizeKnown, - "on_app_read_reset", - Err( + on_app_read_reset: Err( InvalidTransition { - current: SizeKnown, + current: DataRead, event: "on_app_read_reset", }, ), - ), - ( - DataRecvd, - "on_receive_fin", - Err( + }, + DataRecvd: { + on_receive_fin: Err( InvalidTransition { current: DataRecvd, event: "on_receive_fin", }, ), - ), - ( - DataRecvd, - "on_receive_all_data", - Err( + on_receive_all_data: Err( NoOp { current: DataRecvd, }, ), - ), - ( - DataRecvd, - "on_app_read_all_data", - Ok( + on_app_read_all_data: Ok( DataRead, ), - ), - ( - DataRecvd, - "on_reset", - Err( + on_reset: Err( InvalidTransition { current: DataRecvd, event: "on_reset", }, ), - ), - ( - DataRecvd, - "on_app_read_reset", - Err( + on_app_read_reset: Err( InvalidTransition { current: DataRecvd, event: "on_app_read_reset", }, ), - ), - ( - DataRead, - "on_receive_fin", - Err( + }, + Recv: { + on_receive_fin: Ok( + SizeKnown, + ), + on_receive_all_data: Err( InvalidTransition { - current: DataRead, + current: Recv, + event: "on_receive_all_data", + }, + ), + on_app_read_all_data: Err( + InvalidTransition { + current: Recv, + event: "on_app_read_all_data", + }, + ), + on_reset: Ok( + ResetRecvd, + ), + on_app_read_reset: Err( + InvalidTransition { + current: Recv, + event: "on_app_read_reset", + }, + ), + }, + ResetRead: { + on_receive_fin: Err( + InvalidTransition { + current: ResetRead, event: "on_receive_fin", }, ), - ), - ( - DataRead, - "on_receive_all_data", - Err( + on_receive_all_data: Err( InvalidTransition { - current: DataRead, + current: ResetRead, event: "on_receive_all_data", }, ), - ), - ( - DataRead, - "on_app_read_all_data", - Err( - NoOp { - current: DataRead, + on_app_read_all_data: Err( + InvalidTransition { + current: ResetRead, + event: "on_app_read_all_data", }, ), - ), - ( - DataRead, - "on_reset", - Err( + on_reset: Err( InvalidTransition { - current: DataRead, + current: ResetRead, event: "on_reset", }, ), - ), - ( - DataRead, - "on_app_read_reset", - Err( - InvalidTransition { - current: DataRead, - event: "on_app_read_reset", + on_app_read_reset: Err( + NoOp { + current: ResetRead, }, ), - ), - ( - ResetRecvd, - "on_receive_fin", - Err( + }, + ResetRecvd: { + on_receive_fin: Err( InvalidTransition { current: ResetRecvd, event: "on_receive_fin", }, ), - ), - ( - ResetRecvd, - "on_receive_all_data", - Err( + on_receive_all_data: Err( InvalidTransition { current: ResetRecvd, event: "on_receive_all_data", }, ), - ), - ( - ResetRecvd, - "on_app_read_all_data", - Err( + on_app_read_all_data: Err( InvalidTransition { current: ResetRecvd, event: "on_app_read_all_data", }, ), - ), - ( - ResetRecvd, - "on_reset", - Err( + on_reset: Err( NoOp { current: ResetRecvd, }, ), - ), - ( - ResetRecvd, - "on_app_read_reset", - Ok( + on_app_read_reset: Ok( ResetRead, ), - ), - ( - ResetRead, - "on_receive_fin", - Err( - InvalidTransition { - current: ResetRead, - event: "on_receive_fin", + }, + SizeKnown: { + on_receive_fin: Err( + NoOp { + current: SizeKnown, }, ), - ), - ( - ResetRead, - "on_receive_all_data", - Err( - InvalidTransition { - current: ResetRead, - event: "on_receive_all_data", - }, + on_receive_all_data: Ok( + DataRecvd, ), - ), - ( - ResetRead, - "on_app_read_all_data", - Err( + on_app_read_all_data: Err( InvalidTransition { - current: ResetRead, + current: SizeKnown, event: "on_app_read_all_data", }, ), - ), - ( - ResetRead, - "on_reset", - Err( - InvalidTransition { - current: ResetRead, - event: "on_reset", - }, + on_reset: Ok( + ResetRecvd, ), - ), - ( - ResetRead, - "on_app_read_reset", - Err( - NoOp { - current: ResetRead, + on_app_read_reset: Err( + InvalidTransition { + current: SizeKnown, + event: "on_app_read_reset", }, ), - ), -] + }, +} diff --git a/quic/s2n-quic-core/src/stream/state/snapshots/s2n_quic_core__stream__state__send__tests__snapshots.snap b/quic/s2n-quic-core/src/stream/state/snapshots/s2n_quic_core__stream__state__send__tests__snapshots.snap index 34db66ea1e..c540e87bc2 100644 --- a/quic/s2n-quic-core/src/stream/state/snapshots/s2n_quic_core__stream__state__send__tests__snapshots.snap +++ b/quic/s2n-quic-core/src/stream/state/snapshots/s2n_quic_core__stream__state__send__tests__snapshots.snap @@ -1,381 +1,227 @@ --- source: quic/s2n-quic-core/src/stream/state/send.rs -expression: outcomes +expression: "Sender::test_transitions()" --- -[ - ( - Ready, - "on_send_stream", - Ok( - Send, - ), - ), - ( - Ready, - "on_send_fin", - Ok( - DataSent, - ), - ), - ( - Ready, - "on_queue_reset", - Ok( - ResetQueued, - ), - ), - ( - Ready, - "on_send_reset", - Ok( - ResetSent, - ), - ), - ( - Ready, - "on_recv_all_acks", - Err( +{ + DataRecvd: { + on_send_stream: Err( InvalidTransition { - current: Ready, - event: "on_recv_all_acks", + current: DataRecvd, + event: "on_send_stream", }, ), - ), - ( - Ready, - "on_recv_reset_ack", - Err( + on_send_fin: Err( InvalidTransition { - current: Ready, - event: "on_recv_reset_ack", + current: DataRecvd, + event: "on_send_fin", }, ), - ), - ( - Send, - "on_send_stream", - Err( + on_recv_all_acks: Err( NoOp { - current: Send, + current: DataRecvd, }, ), - ), - ( - Send, - "on_send_fin", - Ok( - DataSent, - ), - ), - ( - Send, - "on_queue_reset", - Ok( - ResetQueued, - ), - ), - ( - Send, - "on_send_reset", - Ok( - ResetSent, + on_queue_reset: Err( + InvalidTransition { + current: DataRecvd, + event: "on_queue_reset", + }, ), - ), - ( - Send, - "on_recv_all_acks", - Err( + on_send_reset: Err( InvalidTransition { - current: Send, - event: "on_recv_all_acks", + current: DataRecvd, + event: "on_send_reset", }, ), - ), - ( - Send, - "on_recv_reset_ack", - Err( + on_recv_reset_ack: Err( InvalidTransition { - current: Send, + current: DataRecvd, event: "on_recv_reset_ack", }, ), - ), - ( - DataSent, - "on_send_stream", - Err( + }, + DataSent: { + on_send_stream: Err( InvalidTransition { current: DataSent, event: "on_send_stream", }, ), - ), - ( - DataSent, - "on_send_fin", - Err( + on_send_fin: Err( NoOp { current: DataSent, }, ), - ), - ( - DataSent, - "on_queue_reset", - Ok( + on_recv_all_acks: Ok( + DataRecvd, + ), + on_queue_reset: Ok( ResetQueued, ), - ), - ( - DataSent, - "on_send_reset", - Ok( + on_send_reset: Ok( ResetSent, ), - ), - ( - DataSent, - "on_recv_all_acks", - Ok( - DataRecvd, - ), - ), - ( - DataSent, - "on_recv_reset_ack", - Err( + on_recv_reset_ack: Err( InvalidTransition { current: DataSent, event: "on_recv_reset_ack", }, ), - ), - ( - DataRecvd, - "on_send_stream", - Err( - InvalidTransition { - current: DataRecvd, - event: "on_send_stream", - }, + }, + Ready: { + on_send_stream: Ok( + Send, ), - ), - ( - DataRecvd, - "on_send_fin", - Err( - InvalidTransition { - current: DataRecvd, - event: "on_send_fin", - }, + on_send_fin: Ok( + DataSent, ), - ), - ( - DataRecvd, - "on_queue_reset", - Err( + on_recv_all_acks: Err( InvalidTransition { - current: DataRecvd, - event: "on_queue_reset", + current: Ready, + event: "on_recv_all_acks", }, ), - ), - ( - DataRecvd, - "on_send_reset", - Err( - InvalidTransition { - current: DataRecvd, - event: "on_send_reset", - }, + on_queue_reset: Ok( + ResetQueued, ), - ), - ( - DataRecvd, - "on_recv_all_acks", - Err( - NoOp { - current: DataRecvd, - }, + on_send_reset: Ok( + ResetSent, ), - ), - ( - DataRecvd, - "on_recv_reset_ack", - Err( + on_recv_reset_ack: Err( InvalidTransition { - current: DataRecvd, + current: Ready, event: "on_recv_reset_ack", }, ), - ), - ( - ResetQueued, - "on_send_stream", - Err( + }, + ResetQueued: { + on_send_stream: Err( InvalidTransition { current: ResetQueued, event: "on_send_stream", }, ), - ), - ( - ResetQueued, - "on_send_fin", - Err( + on_send_fin: Err( InvalidTransition { current: ResetQueued, event: "on_send_fin", }, ), - ), - ( - ResetQueued, - "on_queue_reset", - Err( + on_recv_all_acks: Ok( + DataRecvd, + ), + on_queue_reset: Err( NoOp { current: ResetQueued, }, ), - ), - ( - ResetQueued, - "on_send_reset", - Ok( + on_send_reset: Ok( ResetSent, ), - ), - ( - ResetQueued, - "on_recv_all_acks", - Ok( - DataRecvd, - ), - ), - ( - ResetQueued, - "on_recv_reset_ack", - Err( + on_recv_reset_ack: Err( InvalidTransition { current: ResetQueued, event: "on_recv_reset_ack", }, ), - ), - ( - ResetSent, - "on_send_stream", - Err( + }, + ResetRecvd: { + on_send_stream: Err( InvalidTransition { - current: ResetSent, + current: ResetRecvd, event: "on_send_stream", }, ), - ), - ( - ResetSent, - "on_send_fin", - Err( + on_send_fin: Err( InvalidTransition { - current: ResetSent, + current: ResetRecvd, event: "on_send_fin", }, ), - ), - ( - ResetSent, - "on_queue_reset", - Err( + on_recv_all_acks: Err( InvalidTransition { - current: ResetSent, - event: "on_queue_reset", + current: ResetRecvd, + event: "on_recv_all_acks", }, ), - ), - ( - ResetSent, - "on_send_reset", - Err( - NoOp { - current: ResetSent, + on_queue_reset: Err( + InvalidTransition { + current: ResetRecvd, + event: "on_queue_reset", }, ), - ), - ( - ResetSent, - "on_recv_all_acks", - Err( + on_send_reset: Err( InvalidTransition { - current: ResetSent, - event: "on_recv_all_acks", + current: ResetRecvd, + event: "on_send_reset", }, ), - ), - ( - ResetSent, - "on_recv_reset_ack", - Ok( - ResetRecvd, + on_recv_reset_ack: Err( + NoOp { + current: ResetRecvd, + }, ), - ), - ( - ResetRecvd, - "on_send_stream", - Err( + }, + ResetSent: { + on_send_stream: Err( InvalidTransition { - current: ResetRecvd, + current: ResetSent, event: "on_send_stream", }, ), - ), - ( - ResetRecvd, - "on_send_fin", - Err( + on_send_fin: Err( InvalidTransition { - current: ResetRecvd, + current: ResetSent, event: "on_send_fin", }, ), - ), - ( - ResetRecvd, - "on_queue_reset", - Err( + on_recv_all_acks: Err( InvalidTransition { - current: ResetRecvd, - event: "on_queue_reset", + current: ResetSent, + event: "on_recv_all_acks", }, ), - ), - ( - ResetRecvd, - "on_send_reset", - Err( + on_queue_reset: Err( InvalidTransition { - current: ResetRecvd, - event: "on_send_reset", + current: ResetSent, + event: "on_queue_reset", + }, + ), + on_send_reset: Err( + NoOp { + current: ResetSent, + }, + ), + on_recv_reset_ack: Ok( + ResetRecvd, + ), + }, + Send: { + on_send_stream: Err( + NoOp { + current: Send, }, ), - ), - ( - ResetRecvd, - "on_recv_all_acks", - Err( + on_send_fin: Ok( + DataSent, + ), + on_recv_all_acks: Err( InvalidTransition { - current: ResetRecvd, + current: Send, event: "on_recv_all_acks", }, ), - ), - ( - ResetRecvd, - "on_recv_reset_ack", - Err( - NoOp { - current: ResetRecvd, + on_queue_reset: Ok( + ResetQueued, + ), + on_send_reset: Ok( + ResetSent, + ), + on_recv_reset_ack: Err( + InvalidTransition { + current: Send, + event: "on_recv_reset_ack", }, ), - ), -] + }, +}