1use crate::coord::Shift;
2use crate::drawing::area::IntoDrawingArea;
3use crate::drawing::DrawingArea;
4use crate::style::RGBAColor;
5use plotters_backend::{
6 BackendColor, BackendCoord, BackendStyle, BackendTextStyle, DrawingBackend, DrawingErrorKind,
7};
8
9use std::collections::VecDeque;
10
11pub fn check_color(left: BackendColor, right: RGBAColor) {
12 assert_eq!(
13 RGBAColor(left.rgb.0, left.rgb.1, left.rgb.2, left.alpha),
14 right
15 );
16}
17
18pub struct MockedBackend {
19 height: u32,
20 width: u32,
21 init_count: u32,
22 pub draw_count: u32,
23 pub num_draw_pixel_call: u32,
24 pub num_draw_line_call: u32,
25 pub num_draw_rect_call: u32,
26 pub num_draw_circle_call: u32,
27 pub num_draw_text_call: u32,
28 pub num_draw_path_call: u32,
29 pub num_fill_polygon_call: u32,
30 check_draw_pixel: VecDeque<Box<dyn FnMut(RGBAColor, BackendCoord)>>,
31 check_draw_line: VecDeque<Box<dyn FnMut(RGBAColor, u32, BackendCoord, BackendCoord)>>,
32 check_draw_rect: VecDeque<Box<dyn FnMut(RGBAColor, u32, bool, BackendCoord, BackendCoord)>>,
33 check_draw_path: VecDeque<Box<dyn FnMut(RGBAColor, u32, Vec<BackendCoord>)>>,
34 check_draw_circle: VecDeque<Box<dyn FnMut(RGBAColor, u32, bool, BackendCoord, u32)>>,
35 check_draw_text: VecDeque<Box<dyn FnMut(RGBAColor, &str, f64, BackendCoord, &str)>>,
36 check_fill_polygon: VecDeque<Box<dyn FnMut(RGBAColor, Vec<BackendCoord>)>>,
37 drop_check: Option<Box<dyn FnMut(&Self)>>,
38}
39
40macro_rules! def_set_checker_func {
41 (drop_check, $($param:ty),*) => {
42 pub fn drop_check<T: FnMut($($param,)*) + 'static>(&mut self, check:T) -> &mut Self {
43 self.drop_check = Some(Box::new(check));
44 self
45 }
46 };
47 ($name:ident, $($param:ty),*) => {
48 pub fn $name<T: FnMut($($param,)*) + 'static>(&mut self, check:T) -> &mut Self {
49 self.$name.push_back(Box::new(check));
50 self
51 }
52 }
53}
54
55impl MockedBackend {
56 pub fn new(width: u32, height: u32) -> Self {
57 MockedBackend {
58 height,
59 width,
60 init_count: 0,
61 draw_count: 0,
62 num_draw_pixel_call: 0,
63 num_draw_line_call: 0,
64 num_draw_rect_call: 0,
65 num_draw_circle_call: 0,
66 num_draw_text_call: 0,
67 num_draw_path_call: 0,
68 num_fill_polygon_call: 0,
69 check_draw_pixel: vec![].into(),
70 check_draw_line: vec![].into(),
71 check_draw_rect: vec![].into(),
72 check_draw_path: vec![].into(),
73 check_draw_circle: vec![].into(),
74 check_draw_text: vec![].into(),
75 check_fill_polygon: vec![].into(),
76 drop_check: None,
77 }
78 }
79
80 def_set_checker_func!(check_draw_pixel, RGBAColor, BackendCoord);
81 def_set_checker_func!(check_draw_line, RGBAColor, u32, BackendCoord, BackendCoord);
82 def_set_checker_func!(
83 check_draw_rect,
84 RGBAColor,
85 u32,
86 bool,
87 BackendCoord,
88 BackendCoord
89 );
90 def_set_checker_func!(check_draw_path, RGBAColor, u32, Vec<BackendCoord>);
91 def_set_checker_func!(check_draw_circle, RGBAColor, u32, bool, BackendCoord, u32);
92 def_set_checker_func!(check_draw_text, RGBAColor, &str, f64, BackendCoord, &str);
93 def_set_checker_func!(drop_check, &Self);
94 def_set_checker_func!(check_fill_polygon, RGBAColor, Vec<BackendCoord>);
95
96 fn check_before_draw(&mut self) {
97 self.draw_count += 1;
98 //assert_eq!(self.init_count, self.draw_count);
99 }
100}
101
102#[derive(Debug)]
103pub struct MockedError;
104
105impl std::fmt::Display for MockedError {
106 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
107 write!(fmt, "MockedError")
108 }
109}
110
111impl std::error::Error for MockedError {}
112
113impl DrawingBackend for MockedBackend {
114 type ErrorType = MockedError;
115
116 fn get_size(&self) -> (u32, u32) {
117 (self.width, self.height)
118 }
119
120 fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<MockedError>> {
121 self.init_count += 1;
122 Ok(())
123 }
124
125 fn present(&mut self) -> Result<(), DrawingErrorKind<MockedError>> {
126 self.init_count = 0;
127 self.draw_count = 0;
128 Ok(())
129 }
130
131 fn draw_pixel(
132 &mut self,
133 point: BackendCoord,
134 color: BackendColor,
135 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
136 self.check_before_draw();
137 self.num_draw_pixel_call += 1;
138 let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
139 if let Some(mut checker) = self.check_draw_pixel.pop_front() {
140 checker(color, point);
141
142 if self.check_draw_pixel.is_empty() {
143 self.check_draw_pixel.push_back(checker);
144 }
145 }
146 Ok(())
147 }
148
149 fn draw_line<S: BackendStyle>(
150 &mut self,
151 from: BackendCoord,
152 to: BackendCoord,
153 style: &S,
154 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
155 self.check_before_draw();
156 self.num_draw_line_call += 1;
157 let color = style.color();
158 let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
159 if let Some(mut checker) = self.check_draw_line.pop_front() {
160 checker(color, style.stroke_width(), from, to);
161
162 if self.check_draw_line.is_empty() {
163 self.check_draw_line.push_back(checker);
164 }
165 }
166 Ok(())
167 }
168
169 fn draw_rect<S: BackendStyle>(
170 &mut self,
171 upper_left: BackendCoord,
172 bottom_right: BackendCoord,
173 style: &S,
174 fill: bool,
175 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
176 self.check_before_draw();
177 self.num_draw_rect_call += 1;
178 let color = style.color();
179 let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
180 if let Some(mut checker) = self.check_draw_rect.pop_front() {
181 checker(color, style.stroke_width(), fill, upper_left, bottom_right);
182
183 if self.check_draw_rect.is_empty() {
184 self.check_draw_rect.push_back(checker);
185 }
186 }
187 Ok(())
188 }
189
190 fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
191 &mut self,
192 path: I,
193 style: &S,
194 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
195 self.check_before_draw();
196 self.num_draw_path_call += 1;
197 let color = style.color();
198 let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
199 if let Some(mut checker) = self.check_draw_path.pop_front() {
200 checker(color, style.stroke_width(), path.into_iter().collect());
201
202 if self.check_draw_path.is_empty() {
203 self.check_draw_path.push_back(checker);
204 }
205 }
206 Ok(())
207 }
208
209 fn draw_circle<S: BackendStyle>(
210 &mut self,
211 center: BackendCoord,
212 radius: u32,
213 style: &S,
214 fill: bool,
215 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
216 self.check_before_draw();
217 self.num_draw_circle_call += 1;
218 let color = style.color();
219 let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
220 if let Some(mut checker) = self.check_draw_circle.pop_front() {
221 checker(color, style.stroke_width(), fill, center, radius);
222
223 if self.check_draw_circle.is_empty() {
224 self.check_draw_circle.push_back(checker);
225 }
226 }
227 Ok(())
228 }
229
230 fn fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
231 &mut self,
232 path: I,
233 style: &S,
234 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
235 self.check_before_draw();
236 self.num_fill_polygon_call += 1;
237 let color = style.color();
238 let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
239 if let Some(mut checker) = self.check_fill_polygon.pop_front() {
240 checker(color, path.into_iter().collect());
241
242 if self.check_fill_polygon.is_empty() {
243 self.check_fill_polygon.push_back(checker);
244 }
245 }
246 Ok(())
247 }
248
249 fn draw_text<S: BackendTextStyle>(
250 &mut self,
251 text: &str,
252 style: &S,
253 pos: BackendCoord,
254 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
255 let color = style.color();
256 let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha);
257 self.check_before_draw();
258 self.num_draw_text_call += 1;
259 if let Some(mut checker) = self.check_draw_text.pop_front() {
260 checker(color, style.family().as_str(), style.size(), pos, text);
261
262 if self.check_draw_text.is_empty() {
263 self.check_draw_text.push_back(checker);
264 }
265 }
266 Ok(())
267 }
268}
269
270impl Drop for MockedBackend {
271 fn drop(&mut self) {
272 // `self.drop_check` is typically a testing function; it can panic.
273 // The current `drop` call may be a part of stack unwinding caused
274 // by another panic. If so, we should never call it.
275 if std::thread::panicking() {
276 return;
277 }
278
279 let mut temp = None;
280 std::mem::swap(&mut temp, &mut self.drop_check);
281
282 if let Some(mut checker) = temp {
283 checker(self);
284 }
285 }
286}
287
288pub fn create_mocked_drawing_area<F: FnOnce(&mut MockedBackend)>(
289 width: u32,
290 height: u32,
291 setup: F,
292) -> DrawingArea<MockedBackend, Shift> {
293 let mut backend = MockedBackend::new(width, height);
294 setup(&mut backend);
295 backend.into_drawing_area()
296}
297