use core::cell::{Cell, RefCell};
use hdmi_hal::scdc::ScdcTransport;
const LOG_LEN: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Op {
Read(u8),
Write(u8, u8),
ReadBlock(u8, usize),
}
pub struct TestTransport {
pub regs: [u8; 256],
fail_after: usize,
ops: Cell<usize>,
log: RefCell<[Option<Op>; LOG_LEN]>,
}
impl TestTransport {
pub fn new() -> Self {
Self {
regs: [0u8; 256],
fail_after: usize::MAX,
ops: Cell::new(0),
log: RefCell::new([None; LOG_LEN]),
}
}
pub fn failing_after(n: usize) -> Self {
Self {
regs: [0u8; 256],
fail_after: n,
ops: Cell::new(0),
log: RefCell::new([None; LOG_LEN]),
}
}
pub fn set(&mut self, addr: u8, val: u8) {
self.regs[addr as usize] = val;
}
pub fn get(&self, addr: u8) -> u8 {
self.regs[addr as usize]
}
pub fn log(&self) -> [Option<Op>; LOG_LEN] {
*self.log.borrow()
}
fn record(&self, op: Op) {
let n = self.ops.get();
if n < LOG_LEN {
self.log.borrow_mut()[n] = Some(op);
}
self.ops.set(n + 1);
}
}
impl ScdcTransport for TestTransport {
type Error = ();
fn read(&self, reg: u8) -> Result<u8, ()> {
if self.ops.get() >= self.fail_after {
return Err(());
}
self.record(Op::Read(reg));
Ok(self.regs[reg as usize])
}
fn write(&mut self, reg: u8, value: u8) -> Result<(), ()> {
if self.ops.get() >= self.fail_after {
return Err(());
}
self.record(Op::Write(reg, value));
self.regs[reg as usize] = value;
Ok(())
}
fn read_block(&self, reg: u8, buf: &mut [u8]) -> Result<(), ()> {
if buf.is_empty() {
return Ok(());
}
if self.ops.get() >= self.fail_after {
return Err(());
}
self.record(Op::ReadBlock(reg, buf.len()));
let mut addr = reg;
for byte in buf {
*byte = self.regs[addr as usize];
addr = addr.wrapping_add(1);
}
Ok(())
}
}
#[test]
fn the_log_keeps_the_first_operations() {
let transport = TestTransport::new();
for reg in 0..=LOG_LEN as u8 {
transport.read(reg).unwrap();
}
let log = transport.log();
assert_eq!(log[0], Some(Op::Read(0)));
assert_eq!(log[LOG_LEN - 1], Some(Op::Read(LOG_LEN as u8 - 1)));
}
#[test]
fn a_block_read_is_one_operation() {
let mut transport = TestTransport::new();
transport.set(0x50, 0x11);
transport.set(0x51, 0x22);
let mut buf = [0; 2];
transport.read_block(0x50, &mut buf).unwrap();
assert_eq!(buf, [0x11, 0x22]);
assert_eq!(transport.log()[..2], [Some(Op::ReadBlock(0x50, 2)), None]);
}
#[test]
fn a_block_read_counts_once_towards_failing_after() {
let transport = TestTransport::failing_after(1);
let mut buf = [0; 9];
transport.read_block(0x50, &mut buf).unwrap();
assert_eq!(transport.read_block(0x50, &mut buf), Err(()));
assert_eq!(transport.read(0x50), Err(()));
}
#[test]
fn an_empty_block_read_is_no_operation() {
let transport = TestTransport::failing_after(0);
assert_eq!(transport.read_block(0x50, &mut []), Ok(()));
assert_eq!(transport.log()[0], None);
}
#[test]
fn a_block_read_wraps_past_0xff() {
let mut transport = TestTransport::new();
transport.set(0xFF, 0xAA);
transport.set(0x00, 0xBB);
let mut buf = [0; 2];
transport.read_block(0xFF, &mut buf).unwrap();
assert_eq!(buf, [0xAA, 0xBB]);
}