blob: da6bfecc00e9afbdeb9814f47261a91b8cd93228 [file] [log] [blame]
use crate::coord::Shift;
use crate::drawing::area::IntoDrawingArea;
use crate::drawing::DrawingArea;
use crate::style::RGBAColor;
use plotters_backend::{
BackendColor, BackendCoord, BackendStyle, BackendTextStyle, DrawingBackend, DrawingErrorKind,
};
use std::collections::VecDeque;
pub fn check_color(left: BackendColor, right: RGBAColor) {
assert_eq!(
RGBAColor(left.rgb.0, left.rgb.1, left.rgb.2, left.alpha),
right
);
}
pub struct MockedBackend {
height: u32,
width: u32,
init_count: u32,
pub draw_count: u32,
pub num_draw_pixel_call: u32,
pub num_draw_line_call: u32,
pub num_draw_rect_call: u32,
pub num_draw_circle_call: u32,
pub num_draw_text_call: u32,
pub num_draw_path_call: u32,
pub num_fill_polygon_call: u32,
check_draw_pixel: VecDeque<Box<dyn FnMut(RGBAColor, BackendCoord)>>,
check_draw_line: VecDeque<Box<dyn FnMut(RGBAColor, u32, BackendCoord, BackendCoord)>>,
check_draw_rect: VecDeque<Box<dyn FnMut(RGBAColor, u32, bool, BackendCoord, BackendCoord)>>,
check_draw_path: VecDeque<Box<dyn FnMut(RGBAColor, u32, Vec<BackendCoord>)>>,
check_draw_circle: VecDeque<Box<dyn FnMut(RGBAColor, u32, bool, BackendCoord, u32)>>,
check_draw_text: VecDeque<Box<dyn FnMut(RGBAColor, &str, f64, BackendCoord, &str)>>,
check_fill_polygon: VecDeque<Box<dyn FnMut(RGBAColor, Vec<BackendCoord>)>>,
drop_check: Option<Box<dyn FnMut(&Self)>>,
}
macro_rules! def_set_checker_func {
(drop_check, $($param:ty),*) => {
pub fn drop_check<T: FnMut($($param,)*) + 'static>(&mut self, check:T) -> &mut Self {
self.drop_check = Some(Box::new(check));
self
}
};
($name:ident, $($param:ty),*) => {
pub fn $name<T: FnMut($($param,)*) + 'static>(&mut self, check:T) -> &mut Self {
self.$name.push_back(Box::new(check));
self
}
}
}
impl MockedBackend {
pub fn new(width: u32, height: u32) -> Self {
MockedBackend {
height,
width,
init_count: 0,
draw_count: 0,
num_draw_pixel_call: 0,
num_draw_line_call: 0,
num_draw_rect_call: 0,
num_draw_circle_call: 0,
num_draw_text_call: 0,
num_draw_path_call: 0,
num_fill_polygon_call: 0,
check_draw_pixel: vec![].into(),
check_draw_line: vec![].into(),
check_draw_rect: vec![].into(),
check_draw_path: vec![].into(),
check_draw_circle: vec![].into(),
check_draw_text: vec![].into(),
check_fill_polygon: vec![].into(),
drop_check: None,
}
}
def_set_checker_func!(check_draw_pixel, RGBAColor, BackendCoord);
def_set_checker_func!(check_draw_line, RGBAColor, u32, BackendCoord, BackendCoord);
def_set_checker_func!(
check_draw_rect,
RGBAColor,
u32,
bool,
BackendCoord,
BackendCoord
);
def_set_checker_func!(check_draw_path, RGBAColor, u32, Vec<BackendCoord>);
def_set_checker_func!(check_draw_circle, RGBAColor, u32, bool, BackendCoord, u32);
def_set_checker_func!(check_draw_text, RGBAColor, &str, f64, BackendCoord, &str);
def_set_checker_func!(drop_check, &Self);
def_set_checker_func!(check_fill_polygon, RGBAColor, Vec<BackendCoord>);
fn check_before_draw(&mut self) {
self.draw_count += 1;
//assert_eq!(self.init_count, self.draw_count);
}
}
#[derive(Debug)]
pub struct MockedError;
impl std::fmt::Display for MockedError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "MockedError")
}
}
impl std::error::Error for MockedError {}
impl DrawingBackend for MockedBackend {
type ErrorType = MockedError;
fn get_size(&self) -> (u32, u32) {
(self.width, self.height)
}
fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<MockedError>> {
self.init_count += 1;
Ok(())
}
fn present(&mut self) -> Result<(), DrawingErrorKind<MockedError>> {
self.init_count = 0;
self.draw_count = 0;
Ok(())
}
fn draw_pixel(
&mut self,
point: BackendCoord,
color: BackendColor,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.check_before_draw();
self.num_draw_pixel_call += 1;
let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
if let Some(mut checker) = self.check_draw_pixel.pop_front() {
checker(color, point);
if self.check_draw_pixel.is_empty() {
self.check_draw_pixel.push_back(checker);
}
}
Ok(())
}
fn draw_line<S: BackendStyle>(
&mut self,
from: BackendCoord,
to: BackendCoord,
style: &S,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.check_before_draw();
self.num_draw_line_call += 1;
let color = style.color();
let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
if let Some(mut checker) = self.check_draw_line.pop_front() {
checker(color, style.stroke_width(), from, to);
if self.check_draw_line.is_empty() {
self.check_draw_line.push_back(checker);
}
}
Ok(())
}
fn draw_rect<S: BackendStyle>(
&mut self,
upper_left: BackendCoord,
bottom_right: BackendCoord,
style: &S,
fill: bool,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.check_before_draw();
self.num_draw_rect_call += 1;
let color = style.color();
let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
if let Some(mut checker) = self.check_draw_rect.pop_front() {
checker(color, style.stroke_width(), fill, upper_left, bottom_right);
if self.check_draw_rect.is_empty() {
self.check_draw_rect.push_back(checker);
}
}
Ok(())
}
fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
&mut self,
path: I,
style: &S,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.check_before_draw();
self.num_draw_path_call += 1;
let color = style.color();
let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
if let Some(mut checker) = self.check_draw_path.pop_front() {
checker(color, style.stroke_width(), path.into_iter().collect());
if self.check_draw_path.is_empty() {
self.check_draw_path.push_back(checker);
}
}
Ok(())
}
fn draw_circle<S: BackendStyle>(
&mut self,
center: BackendCoord,
radius: u32,
style: &S,
fill: bool,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.check_before_draw();
self.num_draw_circle_call += 1;
let color = style.color();
let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
if let Some(mut checker) = self.check_draw_circle.pop_front() {
checker(color, style.stroke_width(), fill, center, radius);
if self.check_draw_circle.is_empty() {
self.check_draw_circle.push_back(checker);
}
}
Ok(())
}
fn fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
&mut self,
path: I,
style: &S,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.check_before_draw();
self.num_fill_polygon_call += 1;
let color = style.color();
let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
if let Some(mut checker) = self.check_fill_polygon.pop_front() {
checker(color, path.into_iter().collect());
if self.check_fill_polygon.is_empty() {
self.check_fill_polygon.push_back(checker);
}
}
Ok(())
}
fn draw_text<S: BackendTextStyle>(
&mut self,
text: &str,
style: &S,
pos: BackendCoord,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
let color = style.color();
let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
self.check_before_draw();
self.num_draw_text_call += 1;
if let Some(mut checker) = self.check_draw_text.pop_front() {
checker(color, style.family().as_str(), style.size(), pos, text);
if self.check_draw_text.is_empty() {
self.check_draw_text.push_back(checker);
}
}
Ok(())
}
}
impl Drop for MockedBackend {
fn drop(&mut self) {
let mut temp = None;
std::mem::swap(&mut temp, &mut self.drop_check);
if let Some(mut checker) = temp {
checker(self);
}
}
}
pub fn create_mocked_drawing_area<F: FnOnce(&mut MockedBackend)>(
width: u32,
height: u32,
setup: F,
) -> DrawingArea<MockedBackend, Shift> {
let mut backend = MockedBackend::new(width, height);
setup(&mut backend);
backend.into_drawing_area()
}