use crate::error::Result;
use crate::infrastructure::programmer::Programmer;
use std::cell::RefCell;
use std::collections::VecDeque;
#[derive(Debug)]
pub struct MockProgrammer {
name: String,
cs_active: RefCell<bool>,
write_log: RefCell<Vec<Vec<u8>>>,
read_responses: RefCell<VecDeque<Vec<u8>>>,
transaction_log: RefCell<Vec<Transaction>>,
}
#[derive(Debug, Clone)]
pub enum Transaction {
CsActive(bool),
Write(Vec<u8>),
Read { len: usize, data: Vec<u8> },
Transfer { tx: Vec<u8>, rx: Vec<u8> },
}
impl MockProgrammer {
pub fn new() -> Self {
Self {
name: "MockProgrammer".to_string(),
cs_active: RefCell::new(false),
write_log: RefCell::new(Vec::new()),
read_responses: RefCell::new(VecDeque::new()),
transaction_log: RefCell::new(Vec::new()),
}
}
pub fn with_name(mut self, name: &str) -> Self {
self.name = name.to_string();
self
}
pub fn expect_read(&self, data: Vec<u8>) {
self.read_responses.borrow_mut().push_back(data);
}
pub fn expect_reads(&self, responses: Vec<Vec<u8>>) {
for data in responses {
self.expect_read(data);
}
}
pub fn get_writes(&self) -> Vec<Vec<u8>> {
self.write_log.borrow().clone()
}
pub fn get_transactions(&self) -> Vec<Transaction> {
self.transaction_log.borrow().clone()
}
pub fn is_cs_active(&self) -> bool {
*self.cs_active.borrow()
}
pub fn reset(&self) {
self.write_log.borrow_mut().clear();
self.read_responses.borrow_mut().clear();
self.transaction_log.borrow_mut().clear();
*self.cs_active.borrow_mut() = false;
}
}
impl Default for MockProgrammer {
fn default() -> Self {
Self::new()
}
}
impl Programmer for MockProgrammer {
fn name(&self) -> &str {
&self.name
}
fn spi_transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<()> {
self.write_log.borrow_mut().push(tx.to_vec());
let response = self
.read_responses
.borrow_mut()
.pop_front()
.unwrap_or_else(|| vec![0xFF; rx.len()]);
let copy_len = rx.len().min(response.len());
rx[..copy_len].copy_from_slice(&response[..copy_len]);
self.transaction_log
.borrow_mut()
.push(Transaction::Transfer {
tx: tx.to_vec(),
rx: rx.to_vec(),
});
Ok(())
}
fn set_cs(&mut self, active: bool) -> Result<()> {
*self.cs_active.borrow_mut() = active;
self.transaction_log
.borrow_mut()
.push(Transaction::CsActive(active));
Ok(())
}
fn spi_read_bulk(&mut self, len: usize) -> Result<Vec<u8>> {
let response = self
.read_responses
.borrow_mut()
.pop_front()
.unwrap_or_else(|| vec![0xFF; len]);
self.transaction_log.borrow_mut().push(Transaction::Read {
len,
data: response.clone(),
});
Ok(response)
}
fn max_bulk_transfer_size(&self) -> usize {
4096 }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mock_programmer_basic() {
let mut mock = MockProgrammer::new();
assert!(!mock.is_cs_active());
mock.set_cs(true).unwrap();
assert!(mock.is_cs_active());
mock.set_cs(false).unwrap();
assert!(!mock.is_cs_active());
}
#[test]
fn test_mock_programmer_transfer() {
let mut mock = MockProgrammer::new();
mock.expect_read(vec![0x9F, 0xEF, 0xAA, 0x21]);
let mut rx = [0u8; 4];
mock.spi_transfer(&[0x9F, 0x00, 0x00, 0x00], &mut rx)
.unwrap();
assert_eq!(rx, [0x9F, 0xEF, 0xAA, 0x21]);
let writes = mock.get_writes();
assert_eq!(writes.len(), 1);
assert_eq!(writes[0], vec![0x9F, 0x00, 0x00, 0x00]);
}
#[test]
fn test_mock_programmer_multiple_reads() {
let mut mock = MockProgrammer::new();
mock.expect_reads(vec![vec![0x01], vec![0x02], vec![0x03]]);
let r1 = mock.spi_read(1).unwrap();
let r2 = mock.spi_read(1).unwrap();
let r3 = mock.spi_read(1).unwrap();
assert_eq!(r1, vec![0x01]);
assert_eq!(r2, vec![0x02]);
assert_eq!(r3, vec![0x03]);
}
}