blob: e3fb375b881478ddb50600d901905b630a58c32f [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! Server unit tests — exercise `Server` methods directly with mock transport.
//!
//! Each test constructs a `Server` with `DroppingBufferSender` (or
//! `BufferSender` when outbound packets are needed) and calls the
//! server API directly. No transport hardware is involved.
mod common;
use std::cell::RefCell;
use mctp::Eid;
use openprot_mctp_api::ResponseCode;
use openprot_mctp_server::{RecvResult, Server, ServerConfig};
use common::{transfer, BufferSender, DroppingBufferSender};
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/// Deliver a message from a sender EID to a receiver server by routing it
/// through a real `Server::send()` call, avoiding any direct Fragmenter API.
///
/// Creates a temporary sender server (EID `src`) with a `BufferSender`, sends
/// one message of `msg_type` to `dst_eid`, then feeds the captured packets
/// into `dest` via `inbound`.
fn deliver_to<S: mctp_lib::Sender, const N: usize>(
src: u8,
dst_eid: u8,
msg_type: u8,
payload: &[u8],
dest: &mut Server<S, N>,
) {
let buf = RefCell::new(Vec::new());
let mut sender_server: Server<BufferSender<'_>, 16> =
Server::new(Eid(src), 0, BufferSender { packets: &buf });
let req_handle = sender_server.req(dst_eid).unwrap();
sender_server
.send(Some(req_handle), msg_type, None, None, false, payload)
.unwrap();
transfer(&buf, dest);
}
// ---------------------------------------------------------------------------
// EID management
// ---------------------------------------------------------------------------
/// `get_eid` returns the EID passed to `Server::new`.
#[test]
fn eid_initial_value() {
let sender = DroppingBufferSender;
let server: Server<_, 16> = Server::new(Eid(8), 0, sender);
assert_eq!(server.get_eid(), 8);
}
/// `set_eid` + `get_eid` round-trip.
#[test]
fn eid_set_get_roundtrip() {
let sender = DroppingBufferSender;
let mut server: Server<_, 16> = Server::new(Eid(0), 0, sender);
server.set_eid(42).expect("set_eid should succeed");
assert_eq!(server.get_eid(), 42);
}
// ---------------------------------------------------------------------------
// Handle allocation / deallocation
// ---------------------------------------------------------------------------
/// `req()` succeeds and `unbind()` releases the handle cleanly.
#[test]
fn req_handle_alloc_and_unbind() {
let sender = DroppingBufferSender;
let mut server: Server<_, 16> = Server::new(Eid(8), 0, sender);
let handle = server.req(42).expect("req should succeed");
server.unbind(handle).expect("unbind should succeed");
}
/// `listener()` succeeds and `unbind()` releases the handle cleanly.
#[test]
fn listener_handle_alloc_and_unbind() {
let sender = DroppingBufferSender;
let mut server: Server<_, 16> = Server::new(Eid(8), 0, sender);
let handle = server.listener(1).expect("listener should succeed");
server.unbind(handle).expect("unbind should succeed");
}
/// Registering a second listener for the same `msg_type` returns `AddrInUse`.
#[test]
fn listener_duplicate_msg_type_returns_addr_in_use() {
let sender = DroppingBufferSender;
let mut server: Server<_, 16> = Server::new(Eid(8), 0, sender);
server.listener(1).expect("first listener should succeed");
let err = server
.listener(1)
.expect_err("duplicate listener should fail");
assert_eq!(err.code, ResponseCode::AddrInUse);
}
/// Two listeners for *different* `msg_type` values both succeed.
#[test]
fn listener_different_types_both_succeed() {
let sender = DroppingBufferSender;
let mut server: Server<_, 16> = Server::new(Eid(8), 0, sender);
let h1 = server.listener(1).expect("listener type 1 should succeed");
let h2 = server.listener(2).expect("listener type 2 should succeed");
assert_ne!(h1, h2);
}
// ---------------------------------------------------------------------------
// try_recv before inbound
// ---------------------------------------------------------------------------
/// `try_recv` returns `None` when no message has been fed via `inbound`.
#[test]
fn try_recv_before_inbound_returns_none() {
let sender = DroppingBufferSender;
let mut server: Server<_, 16> = Server::new(Eid(8), 0, sender);
let handle = server.listener(1).unwrap();
let mut buf = [0u8; 255];
assert!(server.try_recv(handle, &mut buf).is_none());
}
// ---------------------------------------------------------------------------
// inbound → try_recv routing
// ---------------------------------------------------------------------------
/// A raw packet fed via `inbound` is delivered to the matching listener.
#[test]
fn inbound_then_try_recv_delivers_message() {
let buf_out = RefCell::new(Vec::new());
let sender = BufferSender { packets: &buf_out };
let mut server: Server<_, 16> = Server::new(Eid(8), 0, sender);
let listener = server.listener(1).unwrap();
let payload = b"hello";
deliver_to(42, 8, 1, payload, &mut server);
let mut recv_buf = [0u8; 255];
let meta = server
.try_recv(listener, &mut recv_buf)
.expect("message should be available after inbound");
assert_eq!(meta.msg_type, 1);
assert_eq!(meta.remote_eid, 42);
assert_eq!(meta.payload_size, payload.len());
assert_eq!(&recv_buf[..meta.payload_size], payload);
}
/// A packet for msg_type 2 is not delivered to a listener for msg_type 1.
#[test]
fn inbound_wrong_type_not_delivered() {
let sender = DroppingBufferSender;
let mut server: Server<_, 16> = Server::new(Eid(8), 0, sender);
let listener = server.listener(1).unwrap();
deliver_to(42, 8, 2, b"wrong type", &mut server);
let mut buf = [0u8; 255];
assert!(server.try_recv(listener, &mut buf).is_none());
}
// ---------------------------------------------------------------------------
// send with oversized payload
// ---------------------------------------------------------------------------
/// `send` with a payload larger than `MAX_PAYLOAD` returns `NoSpace`.
#[test]
fn send_oversized_payload_returns_no_space() {
let sender = DroppingBufferSender;
let mut server: Server<_, 16> = Server::new(Eid(8), 0, sender);
let req_handle = server.req(42).unwrap();
let big_payload = vec![0u8; ServerConfig::MAX_PAYLOAD + 1];
let err = server
.send(Some(req_handle), 1, None, None, false, &big_payload)
.expect_err("oversized send should fail");
assert_eq!(err.code, ResponseCode::NoSpace);
}
// ---------------------------------------------------------------------------
// register_recv + update timeout
// ---------------------------------------------------------------------------
/// A registered recv with a timeout fires `RecvResult::TimedOut` after the deadline.
#[test]
fn pending_recv_times_out() {
let sender = DroppingBufferSender;
let mut server: Server<_, 16> = Server::new(Eid(8), 0, sender);
let listener = server.listener(1).unwrap();
// Register a recv with a 100 ms timeout, starting at t=0.
server
.register_recv(listener, 100, 0)
.expect("register_recv should succeed");
let mut recv_buf = [0u8; 255];
// At t=50 ms: not yet timed out, no message.
let (_, ready) = server.update(50, &mut recv_buf);
assert!(ready.is_empty(), "should not fire before deadline");
// At t=100 ms: deadline reached.
let (_, ready) = server.update(100, &mut recv_buf);
assert_eq!(ready.len(), 1);
assert!(
matches!(ready[0], (h, RecvResult::TimedOut) if h == listener),
"expected TimedOut for listener handle"
);
}
/// A registered recv that receives a message before the deadline resolves with
/// `RecvResult::Message`, not a timeout.
#[test]
fn pending_recv_fulfilled_before_timeout() {
let buf_out = RefCell::new(Vec::new());
let sender = BufferSender { packets: &buf_out };
let mut server: Server<_, 16> = Server::new(Eid(8), 0, sender);
let listener = server.listener(1).unwrap();
server
.register_recv(listener, 1000, 0)
.expect("register_recv should succeed");
deliver_to(42, 8, 1, b"data", &mut server);
let mut recv_buf = [0u8; 255];
let (_, ready) = server.update(50, &mut recv_buf);
assert_eq!(ready.len(), 1);
assert!(
matches!(ready[0], (h, RecvResult::Message(_)) if h == listener),
"expected Message result"
);
if let (_, RecvResult::Message(meta)) = ready[0] {
assert_eq!(meta.remote_eid, 42);
assert_eq!(meta.msg_type, 1);
}
}