use super::*;
use crate::{CircuitFlowOutcome, CircuitSendOutcome, Switchboard, flow};
use core::convert::Infallible;
struct Board;
impl Switchboard for Board {
type From<'message> = u8;
type To<'message> = u8;
fn route<'message>(&mut self, emission: Self::From<'message>) -> Route<Self::To<'message>> {
match emission {
0 => Route::fanout((1, 2, 3)),
4 => Route::fanout((5, 6)),
7 => Route::one(8),
_ => Route::none(),
}
}
}
#[test]
fn routes_have_exact_static_arity_and_single_extraction_preserves_rejection() {
let none = Route::<u8>::none();
assert!(none.is_empty());
assert_eq!(none.len(), 0);
assert_eq!(none.into_single(), Err(Route::None));
assert_eq!(Route::one(1).into_single(), Ok(1));
let two = Route::fanout((1, 2));
assert_eq!(two.len(), 2);
assert!(!two.is_empty());
assert_eq!(two.into_single(), Err(Route::Two([1, 2])));
assert_eq!(Route::fanout((1, 2, 3)).len(), 3);
assert_eq!(Route::fanout((1, 2, 3, 4)).len(), 4);
assert_eq!(
Route::fanout((1, 2, 3)).into_single(),
Err(Route::Three([1, 2, 3]))
);
assert_eq!(
Route::fanout((1, 2, 3, 4)).into_single(),
Err(Route::Four([1, 2, 3, 4]))
);
}
#[test]
fn exact_capacity_admits_whole_fanout_and_drains_in_declared_order() {
let mut pending = PendingRoutes::<_, 3>::default();
assert_eq!(pending.capacity(), 3);
assert_eq!(pending.admit(Route::fanout((1, 2, 3, 4))), Ok(Some(1)));
assert_eq!(pending.len(), 3);
assert!(!pending.is_empty());
assert_eq!(pending.pop(), Some(2));
assert_eq!(pending.pop(), Some(3));
assert_eq!(pending.pop(), Some(4));
assert_eq!(pending.pop(), None);
assert_eq!(pending.len(), 0);
assert!(pending.is_empty());
}
#[test]
fn nested_fanout_completes_each_immediate_chain_before_its_siblings()
-> Result<(), alloc::string::String> {
let mut pending = PendingRoutes::<_, 3>::new();
let mut trace = alloc::vec::Vec::new();
let result = flow(&mut Board, 0, &mut pending, |destination| {
trace.push(destination);
Ok::<_, Infallible>(match destination {
1 => CircuitSendOutcome::Immediate { from: 4 },
5 => CircuitSendOutcome::Immediate { from: 7 },
8 => CircuitSendOutcome::Immediate { from: 9 },
_ => CircuitSendOutcome::<_, Infallible>::Submitted,
})
})
.map_err(|failure| alloc::format!("{failure:?}"))?;
assert_eq!(result, CircuitFlowOutcome::Submitted);
assert_eq!(trace, [1, 5, 8, 6, 2, 3]);
assert!(pending.is_empty());
Ok(())
}
#[test]
fn capacity_refuses_new_children_without_losing_existing_siblings() -> Result<(), &'static str> {
let mut pending = PendingRoutes::<_, 2>::new();
let mut trace = alloc::vec::Vec::new();
let result = flow(&mut Board, 0, &mut pending, |destination| {
trace.push(destination);
Ok::<_, Infallible>(CircuitSendOutcome::<_, Infallible>::Immediate { from: 4 })
});
let Err(FlowFailure::Capacity {
rejected,
maximum_pending,
}) = result
else {
return Err("expected capacity refusal");
};
assert_eq!(rejected, Route::fanout((5, 6)));
assert_eq!(maximum_pending, 2);
assert_eq!(trace, [1]);
assert_eq!(pending.pop(), Some(2));
assert_eq!(pending.pop(), Some(3));
assert!(pending.is_empty());
Ok(())
}
#[test]
fn zero_capacity_still_conducts_single_destinations_and_finished_branches()
-> Result<(), alloc::string::String> {
let mut pending = PendingRoutes::<_, 0>::new();
let mut trace = alloc::vec::Vec::new();
let outcome = flow(&mut Board, 7, &mut pending, |destination| {
trace.push(destination);
Ok::<_, Infallible>(CircuitSendOutcome::<_, Infallible>::Submitted)
})
.map_err(|failure| alloc::format!("{failure:?}"))?;
assert_eq!(outcome, CircuitFlowOutcome::Submitted);
assert_eq!(trace, [8]);
let result = pending.admit(Route::fanout((1, 2)));
assert_eq!(result, Err(Route::fanout((1, 2))));
Ok(())
}
#[test]
fn dispatch_failure_and_termination_leave_unissued_siblings_owned_by_caller() {
let mut pending = PendingRoutes::<_, 2>::new();
let result = flow(&mut Board, 0, &mut pending, |destination| {
Err::<CircuitSendOutcome<u8, ()>, _>(destination)
});
assert!(matches!(result, Err(FlowFailure::Dispatch(1))));
assert_eq!(pending.pop(), Some(2));
assert_eq!(pending.pop(), Some(3));
let result = flow(&mut Board, 0, &mut pending, |destination| {
Ok::<_, Infallible>(CircuitSendOutcome::Completed {
completion: destination,
})
});
assert!(matches!(
result,
Ok(CircuitFlowOutcome::Completed { completion: 1 })
));
assert_eq!(pending.pop(), Some(2));
assert_eq!(pending.pop(), Some(3));
}
#[test]
fn refusal_is_routed_before_a_healthy_sibling_without_replaying_accepted_work()
-> Result<(), alloc::string::String> {
let mut pending = PendingRoutes::<_, 2>::new();
let mut trace = alloc::vec::Vec::new();
let result = flow(&mut Board, 0, &mut pending, |destination| {
trace.push(destination);
Ok::<_, Infallible>(match destination {
1 => CircuitSendOutcome::Immediate { from: 7 },
_ => CircuitSendOutcome::<_, Infallible>::Submitted,
})
})
.map_err(|failure| alloc::format!("{failure:?}"))?;
assert_eq!(result, CircuitFlowOutcome::Submitted);
assert_eq!(trace, [1, 8, 2, 3]);
Ok(())
}
#[test]
fn lending_and_non_send_destinations_need_no_copy_or_thread_capability()
-> Result<(), alloc::string::String> {
struct Lending;
impl Switchboard for Lending {
type From<'message> = (&'message str, alloc::rc::Rc<()>);
type To<'message> = (&'message str, Option<alloc::rc::Rc<()>>);
fn route<'message>(
&mut self,
(text, owner): Self::From<'message>,
) -> Route<Self::To<'message>> {
Route::fanout(((text, Some(owner)), (text, None)))
}
}
let text = alloc::string::String::from("borrowed");
let owner = alloc::rc::Rc::new(());
let pointer = alloc::rc::Rc::as_ptr(&owner);
let mut seen = alloc::vec::Vec::new();
let mut pending = PendingRoutes::<_, 1>::new();
let outcome = flow(
&mut Lending,
(&text, owner),
&mut pending,
|(borrowed, owner)| {
assert_eq!(borrowed.as_ptr(), text.as_ptr());
seen.push(owner.map(|owner| alloc::rc::Rc::as_ptr(&owner)));
Ok::<_, Infallible>(CircuitSendOutcome::<_, Infallible>::Submitted)
},
)
.map_err(|failure| alloc::format!("{failure:?}"))?;
assert_eq!(outcome, CircuitFlowOutcome::Submitted);
assert_eq!(seen, [Some(pointer), None]);
Ok(())
}
#[test]
fn failure_debug_does_not_require_payload_debug_or_expose_credentials() {
struct Secret;
let failure = FlowFailure::<u8, _>::Capacity {
rejected: Route::one(Secret),
maximum_pending: 0,
};
assert_eq!(
alloc::format!("{failure:?}"),
"Capacity { destinations: 1, maximum_pending: 0 }"
);
assert_eq!(
alloc::format!("{:?}", FlowFailure::<_, Secret>::Dispatch(4)),
"Dispatch(4)"
);
}
#[test]
fn arbitrary_bounded_routes_preserve_the_depth_first_stack_model()
-> Result<(), alloc::string::String> {
use proptest::{prelude::*, test_runner::TestRunner};
let mut runner = TestRunner::default();
runner
.run(
&proptest::collection::vec((0_u8..=4, any::<u16>()), 0..100),
|steps| {
let mut pending = PendingRoutes::<_, 7>::new();
let mut model = alloc::vec::Vec::new();
for (width, value) in steps {
let children = [
value,
value.rotate_left(1),
value.rotate_left(2),
value.rotate_left(3),
];
let route = match (width, children) {
(0, _) => Route::none(),
(1, [a, ..]) => Route::one(a),
(2, [a, b, ..]) => Route::fanout((a, b)),
(3, [a, b, c, ..]) => Route::fanout((a, b, c)),
(_, [a, b, c, d]) => Route::fanout((a, b, c, d)),
};
let siblings = width.checked_sub(1).map_or(0, usize::from);
let [a, b, c, d] = children;
if model
.iter()
.chain(core::iter::repeat_n(&value, siblings))
.count()
> 7
{
prop_assert_eq!(
pending.admit(route).map(|_| ()),
Err(match width {
2 => Route::fanout((a, b)),
3 => Route::fanout((a, b, c)),
_ => Route::fanout((a, b, c, d)),
})
);
} else {
let expected = if width == 0 { None } else { Some(value) };
prop_assert_eq!(pending.admit(route), Ok(expected));
model.extend(children.into_iter().take(usize::from(width)).skip(1).rev());
}
prop_assert_eq!(pending.len(), model.len());
prop_assert_eq!(pending.pop(), model.pop());
}
while let Some(expected) = model.pop() {
prop_assert_eq!(pending.pop(), Some(expected));
}
prop_assert!(pending.is_empty());
Ok(())
},
)
.map_err(|failure| alloc::format!("{failure}"))
}
#[test]
fn mapping_preserves_order_cardinality_and_owned_values() {
for route in [
Route::none(),
Route::one(1),
Route::fanout((1, 2)),
Route::fanout((1, 2, 3)),
Route::fanout((1, 2, 3, 4)),
] {
let count = route.len();
let mut seen = alloc::vec::Vec::new();
let mapped = route.map(|value| {
seen.push(value);
alloc::boxed::Box::new(value)
});
assert_eq!(mapped.len(), count);
assert_eq!(seen, (1..=count).collect::<alloc::vec::Vec<_>>());
mapped.discard();
}
PendingRoutes::<u8, 0>::new().discard();
}
#[test]
fn nested_tuple_forms_keep_left_to_right_order_without_an_intermediate_container() {
let three = Route::Three([1u8, 2, 3]);
assert_eq!(Route::fanout((1, (2, 3))), three);
assert_eq!(Route::fanout(((1, 2), 3)), three);
let four = Route::Four([1u8, 2, 3, 4]);
assert_eq!(Route::fanout(((1, 2), (3, 4))), four);
assert_eq!(Route::fanout((1, (2, (3, 4)))), four);
assert_eq!(Route::fanout((((1, 2), 3), 4)), four);
assert_eq!(Route::fanout(((1, (2, 3)), 4)), four);
assert_eq!(Route::fanout((1, ((2, 3), 4))), four);
}