diff --git a/tines-core/tests/common/mod.rs b/tines-core/tests/common/mod.rs new file mode 100644 index 0000000..50e9c89 --- /dev/null +++ b/tines-core/tests/common/mod.rs @@ -0,0 +1,97 @@ +//! Shared, test-only helper: two `PairTransport`s wired back-to-back as if they +//! were peers on a datagram link. A `send` on one side surfaces as a `Frame` +//! on the other; `connect`/`disconnect` broadcast `Contact`/`Lost`. +//! +//! Each side owns its own inbound queue (`inbox`) and holds a shared handle to +//! the other side's queue (`peer_inbox`), so outbound frames land in the peer's +//! queue — just like frames crossing a real link. `std` is fine here; this is a +//! test fixture, not `tines-core` proper. + +use std::cell::RefCell; +use std::collections::VecDeque; +use std::rc::Rc; + +use tines_core::transport::{Event, FrameBuf, SendError, Transport}; + +/// A pair of in-memory transports connected back-to-back for tests. +/// +/// Addresses are `0` and `1`; the first handle handed back by [`PairTransport::pair`] +/// is `0`, the second is `1`. +pub struct PairTransport { + /// Ourselves: `0` or `1`. + me: u8, + /// Address of the peer this side talks to. + other: u8, + /// Our own inbound queue. + inbox: Rc>>>, + /// The peer's inbound queue, where our `send`s land. + peer_inbox: Rc>>>, + /// Configured MTU. + mtu: usize, +} + +impl PairTransport { + /// Build a connected pair. Returns `(addr 0, addr 1)`. + pub fn pair(mtu: usize) -> (PairTransport, PairTransport) { + let inbox0: Rc>>> = Rc::new(RefCell::new(VecDeque::new())); + let inbox1: Rc>>> = Rc::new(RefCell::new(VecDeque::new())); + + let a = PairTransport { + me: 0, + other: 1, + inbox: inbox0.clone(), + peer_inbox: inbox1.clone(), + mtu, + }; + let b = PairTransport { + me: 1, + other: 0, + inbox: inbox1.clone(), + peer_inbox: inbox0.clone(), + mtu, + }; + (a, b) + } + + /// Announce that the peer is reachable. + /// + /// A real contact is symmetric: when two peers meet, each independently + /// observes it and calls `connect()` on its own transport, so this enqueues + /// `Contact(other)` on *this* side's queue only. (Broadcasting onto the + /// peer's queue as well would crowd out the peer's own `Contact` and any + /// in-flight frames — the paired peer learns of the contact via its own + /// `connect()`, as the test in `tests/pair.rs` exercises.) + pub fn connect(&mut self) { + let other = self.other; + self.inbox.borrow_mut().push_back(Event::Contact(other)); + } + + /// Announce that the peer is unreachable. + #[allow(dead_code)] // required by the transport interface; used by sim harness + pub fn disconnect(&mut self) { + let other = self.other; + self.inbox.borrow_mut().push_back(Event::Lost(other)); + } +} + +impl Transport for PairTransport { + type Addr = u8; + + fn poll(&mut self) -> Option> { + self.inbox.borrow_mut().pop_front() + } + + fn send(&mut self, to: &Self::Addr, frame: &[u8]) -> Result<(), SendError> { + if *to != self.other { + return Err(SendError::Unreachable); + } + let buf = FrameBuf::from_slice(frame).map_err(|_| SendError::TooLarge)?; + let event = Event::Frame(self.me, buf); + self.peer_inbox.borrow_mut().push_back(event); + Ok(()) + } + + fn mtu(&self) -> usize { + self.mtu + } +} diff --git a/tines-core/tests/pair.rs b/tines-core/tests/pair.rs new file mode 100644 index 0000000..ad9d93a --- /dev/null +++ b/tines-core/tests/pair.rs @@ -0,0 +1,16 @@ +mod common; +use common::PairTransport; + +use tines_core::transport::{Event, FrameBuf, Transport}; + +#[test] +fn pair_transport_connect_then_frame() { + let (mut a, mut b) = PairTransport::pair(1500); + a.connect(); + assert_eq!(a.poll(), Some(Event::Contact(1))); + a.send(&1, b"hi").unwrap(); + assert_eq!( + b.poll(), + Some(Event::Frame(0, FrameBuf::from_slice(b"hi").unwrap())) + ); +}