use crate::console::RGB;
use crate::gfx::lcd::fonts::Font;
use crate::gfx::lcd::{AsByteSlice, BufferedLcd, Lcd, LcdSize, LcdXY};
use std::io;
#[cfg(test)]
#[derive(Clone, Copy)]
pub(super) struct RGB888Pixel(pub(super) [u8; 3]);
#[cfg(test)]
impl AsByteSlice for RGB888Pixel {
fn as_slice(&self) -> &[u8] {
&self.0
}
}
pub(super) fn xy(x: usize, y: usize) -> LcdXY {
LcdXY { x, y }
}
pub(super) fn size(width: usize, height: usize) -> LcdSize {
LcdSize { width, height }
}
pub(super) struct LcdRecorder {
size: LcdSize,
ops: Vec<String>,
}
impl LcdRecorder {
pub(crate) fn new(size: LcdSize) -> Self {
Self { size, ops: vec![] }
}
}
impl Lcd for LcdRecorder {
type Pixel = RGB888Pixel;
fn info(&self) -> (LcdSize, usize) {
(self.size, 3)
}
fn encode(&self, rgb: RGB) -> Self::Pixel {
RGB888Pixel([rgb.0, rgb.1, rgb.2])
}
fn set_data(&mut self, x1y1: LcdXY, x2y2: LcdXY, data: &[u8]) -> io::Result<()> {
self.ops.push(format!(
"set_data: from=({}, {}), to=({}, {}), data={:?}",
x1y1.x, x1y1.y, x2y2.x, x2y2.y, data
));
Ok(())
}
}
const FONT_ZERO: Font =
Font { name: "zero", glyph_size: LcdSize { width: 8, height: 8 }, stride: 1, data: &[] };
#[must_use]
pub(super) struct Tester {
size: LcdSize,
buffered: BufferedLcd<LcdRecorder>,
exp_fb: Vec<u8>,
exp_damage: Option<(LcdXY, LcdXY)>,
exp_ops: Vec<String>,
}
impl Tester {
pub(super) fn new(size: LcdSize) -> Self {
Self::with_font(size, &FONT_ZERO)
}
pub(super) fn with_font(size: LcdSize, font: &'static Font) -> Self {
let fb_size = size.width * size.height * 3;
Self {
size,
buffered: BufferedLcd::new(LcdRecorder::new(size), font),
exp_fb: vec![0; fb_size],
exp_damage: None,
exp_ops: vec![],
}
}
pub(super) fn op<F>(mut self, op: F) -> Self
where
F: Fn(&mut BufferedLcd<LcdRecorder>),
{
op(&mut self.buffered);
self
}
pub(super) fn expect_pixel(mut self, xy: LcdXY, rgb: RGB) -> Self {
let offset = ((xy.y * self.size.width) + xy.x) * 3;
self.exp_fb[offset] = rgb.0;
self.exp_fb[offset + 1] = rgb.1;
self.exp_fb[offset + 2] = rgb.2;
self
}
pub(super) fn expect_damage(mut self, x1y1: LcdXY, x2y2: LcdXY) -> Self {
assert!(self.exp_damage.is_none());
self.exp_damage = Some((x1y1, x2y2));
self
}
pub(super) fn expect_op(mut self, op: &str) -> Self {
self.exp_ops.push(op.into());
self
}
pub(super) fn ignore_pixels(mut self) -> Self {
self.exp_fb = self.buffered.fb.clone();
self
}
pub(super) fn check(self) {
if self.exp_fb != self.buffered.fb {
for y in 0..self.size.height {
for x in 0..self.size.width {
let offset = (y * self.size.width + x) * 3;
let exp_pixel = &self.exp_fb[offset..offset + 3];
let pixel = &self.buffered.fb[offset..offset + 3];
if exp_pixel != pixel {
eprintln!(
".expect_pixel(xy({:3}, {:3}), ({:3}, {:3}, {:3})) // got ({:3}, {:3}, {:3})",
x,
y,
pixel[0],
pixel[1],
pixel[2],
exp_pixel[0],
exp_pixel[1],
exp_pixel[2],
);
}
}
}
panic!("Pixel contents differ; see output above");
}
assert_eq!(self.exp_damage, self.buffered.damage);
assert_eq!(self.exp_ops, self.buffered.lcd.ops);
}
}