1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! **mock_datagram** — testing `MessageDatagram` code with the `mock` feature, no real socket.
//!
//! `net`'s [`DatagramSocket`](bnb::DatagramSocket) trait is *sealed* (only `bnb` implements it), so
//! to unit-test datagram logic you reach for [`MockDatagramSocket`](bnb::MockDatagramSocket) (the
//! `mock` feature) instead of binding a real `UdpSocket`. Write your handler **generic over the
//! socket** — sealing forbids *implementing* the trait, not *using it as a bound* — so the same
//! code runs over a real `UdpSocket` in production and the mock in tests. Put `features = ["mock"]`
//! in your crate's `[dev-dependencies]`.
//!
//! Run with: `cargo run -p bitsandbytes --example mock_datagram --features mock`
use bnb::{DatagramSocket, MessageDatagram, MockDatagramSocket, bin};
use std::net::SocketAddr;
#[bin(big)]
#[derive(Debug, PartialEq, Eq)]
struct Request {
id: u16,
op: u8,
}
#[bin(big)]
#[derive(Debug, PartialEq, Eq)]
struct Reply {
id: u16,
status: u8,
}
/// The unit under test — read one request, reply to its sender. Generic over the socket, so it
/// runs unchanged over a real `UdpSocket` (production) or a `MockDatagramSocket` (tests).
fn serve_one<D: DatagramSocket<Addr = SocketAddr>>(
server: &mut MessageDatagram<D>,
) -> Result<(), bnb::BitError> {
let (req, from): (Request, _) = server.recv_message()?;
server.send_message(
&Reply {
id: req.id,
status: 0,
},
&from,
)?;
Ok(())
}
fn main() {
// A server over the mock — nothing bound to the network.
let mut server = MessageDatagram::new(MockDatagramSocket::new());
// Inject a request as if a client had sent it.
let client: SocketAddr = "127.0.0.1:40000".parse().unwrap();
server
.get_ref()
.push_inbound(&Request { id: 42, op: 1 }.to_bytes().unwrap(), client);
// Run the handler, then assert on what it sent.
serve_one(&mut server).unwrap();
let sent = server.get_ref().sent();
assert_eq!(sent.len(), 1);
let (bytes, dest) = &sent[0];
assert_eq!(*dest, client); // replied to the sender
let reply = Reply::decode_exact(bytes).unwrap();
assert_eq!(reply, Reply { id: 42, status: 0 }); // matching id
println!("served one request over a mock socket (no UdpSocket bound):");
println!(" in : Request {{ id: 42, op: 1 }} from {client}");
println!(" out: {reply:?} -> {dest}");
// --- error path: the recv fails ---
// `fail_next_recv` makes the next `recv_from` error — `recv_message` surfaces the I/O error.
let mut down = MessageDatagram::new(MockDatagramSocket::new().fail_next_recv());
let err = down.recv_message::<Request>().unwrap_err();
assert!(matches!(err.kind, bnb::ErrorKind::Io(_)));
println!(" error path: a failed recv surfaces as {:?}", err.kind);
println!("all checks passed");
}