blob: d94dad8d76d5b0ce9a0dbfebea5111002e00fadd [file]
// Licensed under the Apache-2.0 license
//! Wire-protocol dispatch integration test.
//!
//! Exercises the full IPC path: encode request → `dispatch_mctp_op` → decode
//! response. This verifies that the wire protocol + dispatch layer + Server
//! work together correctly, simulating what happens when `IpcMctpClient`
//! talks to the MCTP server process over a Pigweed IPC channel.
use std::cell::RefCell;
use mctp::{Eid, Tag};
use mctp_lib::fragment::{Fragmenter, SendOutput};
use mctp_lib::Sender;
use openprot_mctp_api::wire;
use openprot_mctp_server::{dispatch::dispatch_mctp_op, Server};
// ---------------------------------------------------------------------------
// Mock transport (same as echo.rs)
// ---------------------------------------------------------------------------
struct BufferSender<'a> {
packets: &'a RefCell<Vec<Vec<u8>>>,
}
impl Sender for BufferSender<'_> {
fn send_vectored(
&mut self,
mut fragmenter: Fragmenter,
payload: &[&[u8]],
) -> mctp::Result<Tag> {
loop {
let mut buf = [0u8; 255];
match fragmenter.fragment_vectored(payload, &mut buf) {
SendOutput::Packet(p) => {
self.packets.borrow_mut().push(p.to_vec());
}
SendOutput::Complete { tag, .. } => return Ok(tag),
SendOutput::Error { err, .. } => return Err(err),
}
}
}
fn get_mtu(&self) -> usize {
255
}
}
fn transfer<S: Sender, const N: usize>(
packets: &RefCell<Vec<Vec<u8>>>,
dest: &mut Server<S, N>,
) {
for pkt in packets.borrow().iter() {
dest.inbound(pkt).unwrap();
}
}
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
/// Test SetEid + GetEid via dispatch.
#[test]
fn dispatch_set_get_eid() {
let buf = RefCell::new(Vec::new());
let sender = BufferSender { packets: &buf };
let mut server: Server<_, 16> = Server::new(Eid(0), 0, sender);
let mut req = [0u8; 64];
let mut resp = [0u8; 64];
let mut recv_buf = [0u8; 255];
// SetEid(42)
let req_len = wire::encode_set_eid(&mut req, 42).unwrap();
let resp_len = dispatch_mctp_op(&req[..req_len], &mut resp, &mut server, &mut recv_buf);
let header = wire::decode_response_header(&resp[..resp_len]).unwrap();
assert!(header.is_success());
// GetEid → 42
let req_len = wire::encode_get_eid(&mut req).unwrap();
let resp_len = dispatch_mctp_op(&req[..req_len], &mut resp, &mut server, &mut recv_buf);
let header = wire::decode_response_header(&resp[..resp_len]).unwrap();
assert!(header.is_success());
assert_eq!(header.eid, 42);
}
/// Test Listener + Req + Send + Recv via dispatch (full echo round-trip).
#[test]
fn dispatch_echo_roundtrip() {
// Server A (echo responder, EID 8)
let buf_a = RefCell::new(Vec::new());
let sender_a = BufferSender { packets: &buf_a };
let mut server_a: Server<_, 16> = Server::new(Eid(8), 0, sender_a);
// Server B (requester, EID 42)
let buf_b = RefCell::new(Vec::new());
let sender_b = BufferSender { packets: &buf_b };
let mut server_b: Server<_, 16> = Server::new(Eid(42), 0, sender_b);
let mut req = [0u8; 128];
let mut resp = [0u8; 128];
let mut recv_buf = [0u8; 255];
// Register listener on A for MsgType(1)
let req_len = wire::encode_listener(&mut req, 1).unwrap();
let resp_len = dispatch_mctp_op(&req[..req_len], &mut resp, &mut server_a, &mut recv_buf);
let header = wire::decode_response_header(&resp[..resp_len]).unwrap();
assert!(header.is_success());
let listener_handle = header.handle;
// Register req on B targeting EID 8
let req_len = wire::encode_req(&mut req, 8).unwrap();
let resp_len = dispatch_mctp_op(&req[..req_len], &mut resp, &mut server_b, &mut recv_buf);
let header = wire::decode_response_header(&resp[..resp_len]).unwrap();
assert!(header.is_success());
let req_handle = header.handle;
// B sends a message via dispatch
let payload = b"dispatch echo!";
let req_len = wire::encode_send(
&mut req,
Some(req_handle),
1,
None,
None,
false,
payload,
)
.unwrap();
let resp_len = dispatch_mctp_op(&req[..req_len], &mut resp, &mut server_b, &mut recv_buf);
let header = wire::decode_response_header(&resp[..resp_len]).unwrap();
assert!(header.is_success());
// Transfer B → A
transfer(&buf_b, &mut server_a);
// A receives via dispatch
let req_len = wire::encode_recv(&mut req, listener_handle, 0).unwrap();
let resp_len = dispatch_mctp_op(&req[..req_len], &mut resp, &mut server_a, &mut recv_buf);
let header = wire::decode_response_header(&resp[..resp_len]).unwrap();
assert!(header.is_success());
assert_eq!(header.msg_type, 1);
assert_eq!(header.eid, 42); // remote EID
let recv_payload = wire::get_response_payload(&resp[..resp_len], &header).unwrap();
assert_eq!(recv_payload, payload);
// A echoes back via dispatch (response: no handle, set eid + tag)
let req_len = wire::encode_send(
&mut req,
None,
header.msg_type,
Some(header.eid),
Some(header.tag),
false,
recv_payload,
)
.unwrap();
let resp_len = dispatch_mctp_op(&req[..req_len], &mut resp, &mut server_a, &mut recv_buf);
let send_header = wire::decode_response_header(&resp[..resp_len]).unwrap();
assert!(send_header.is_success());
// Transfer A → B
transfer(&buf_a, &mut server_b);
// B receives the echo via dispatch
let req_len = wire::encode_recv(&mut req, req_handle, 0).unwrap();
let resp_len = dispatch_mctp_op(&req[..req_len], &mut resp, &mut server_b, &mut recv_buf);
let header = wire::decode_response_header(&resp[..resp_len]).unwrap();
assert!(header.is_success());
assert_eq!(header.msg_type, 1);
assert_eq!(header.eid, 8); // from server A
let echo_payload = wire::get_response_payload(&resp[..resp_len], &header).unwrap();
assert_eq!(echo_payload, payload);
}