#![allow(dead_code)]
use core::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;
use embassy_time::Duration as EmbassyDuration;
use futures::FutureExt;
use futures::executor::block_on;
use rfm69_async::{Address, Flags, MacTiming, Packet, Stack, StackResources, Transceiver, TrxError};
#[derive(Clone, Default)]
pub struct MockTrx {
inbox: Rc<RefCell<VecDeque<Result<Packet, TrxError>>>>,
outbox: Rc<RefCell<Vec<Packet>>>,
recover_queue: Rc<RefCell<VecDeque<Result<(), TrxError>>>>,
recover_calls: Rc<RefCell<u32>>,
}
impl MockTrx {
pub fn new() -> Self {
Self::default()
}
pub fn inject(&self, packet: Packet) {
self.inbox.borrow_mut().push_back(Ok(packet));
}
pub fn inject_err(&self, err: TrxError) {
self.inbox.borrow_mut().push_back(Err(err));
}
pub fn inject_recover_ok(&self) {
self.recover_queue.borrow_mut().push_back(Ok(()));
}
pub fn inject_recover_err(&self, err: TrxError) {
self.recover_queue.borrow_mut().push_back(Err(err));
}
pub fn recover_calls(&self) -> u32 {
*self.recover_calls.borrow()
}
pub fn sent(&self) -> Vec<Packet> {
self.outbox.borrow().clone()
}
}
impl Transceiver for MockTrx {
async fn send(&mut self, packet: &Packet) -> Result<(), TrxError> {
self.outbox.borrow_mut().push(packet.clone());
Ok(())
}
async fn recv(&mut self) -> Result<Packet, TrxError> {
loop {
if let Some(item) = self.inbox.borrow_mut().pop_front() {
return item;
}
yield_now().await;
}
}
async fn recover(&mut self) -> Result<(), TrxError> {
*self.recover_calls.borrow_mut() += 1;
self.recover_queue
.borrow_mut()
.pop_front()
.unwrap_or(Err(TrxError::Reset))
}
}
pub fn pkt(src: Address, dst: Address, flags: Flags, data: &[u8]) -> Packet {
Packet::new(src, dst, flags, data).unwrap()
}
pub async fn pace_time(steps: usize, step_ms: u64) {
let driver = embassy_time::MockDriver::get();
for _ in 0..steps {
driver.advance(EmbassyDuration::from_millis(step_ms));
yield_now().await;
}
}
pub fn yield_now() -> impl core::future::Future<Output = ()> {
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
struct Yield(bool);
impl Future for Yield {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if self.0 {
Poll::Ready(())
} else {
self.0 = true;
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
Yield(false)
}
pub fn run_test<Body>(body: Body)
where
Body: for<'a> AsyncFnOnce(Stack<'a>, &'a MockTrx),
{
let mut resources = StackResources::<4>::new();
let trx = MockTrx::new();
let timing = MacTiming {
ack_tx_delay: EmbassyDuration::from_millis(1),
ack_timeout: EmbassyDuration::from_millis(50),
tx_retry_delay: EmbassyDuration::from_millis(10),
recover_backoff: EmbassyDuration::from_millis(500),
};
let (stack, mut runner) = Stack::new(trx.clone(), Address::Unicast(1), &mut resources, timing);
let trx_ref = &trx;
block_on(async move {
let test = body(stack, trx_ref).fuse();
let runner = runner.run().fuse();
futures::pin_mut!(test);
futures::pin_mut!(runner);
futures::select! {
_ = test => {},
_ = runner => unreachable!("runner exited"),
}
});
}