1 use crate::coord::Shift;
2 use crate::drawing::area::IntoDrawingArea;
3 use crate::drawing::DrawingArea;
4 use crate::style::RGBAColor;
5 use plotters_backend::{
6 BackendColor, BackendCoord, BackendStyle, BackendTextStyle, DrawingBackend, DrawingErrorKind,
7 };
8
9 use std::collections::VecDeque;
10
check_color(left: BackendColor, right: RGBAColor)11 pub 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
18 pub 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
40 macro_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
55 impl MockedBackend {
new(width: u32, height: u32) -> Self56 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
check_before_draw(&mut self)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)]
103 pub struct MockedError;
104
105 impl std::fmt::Display for MockedError {
fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result106 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
107 write!(fmt, "MockedError")
108 }
109 }
110
111 impl std::error::Error for MockedError {}
112
113 impl DrawingBackend for MockedBackend {
114 type ErrorType = MockedError;
115
get_size(&self) -> (u32, u32)116 fn get_size(&self) -> (u32, u32) {
117 (self.width, self.height)
118 }
119
ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<MockedError>>120 fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<MockedError>> {
121 self.init_count += 1;
122 Ok(())
123 }
124
present(&mut self) -> Result<(), DrawingErrorKind<MockedError>>125 fn present(&mut self) -> Result<(), DrawingErrorKind<MockedError>> {
126 self.init_count = 0;
127 self.draw_count = 0;
128 Ok(())
129 }
130
draw_pixel( &mut self, point: BackendCoord, color: BackendColor, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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
draw_line<S: BackendStyle>( &mut self, from: BackendCoord, to: BackendCoord, style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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
draw_rect<S: BackendStyle>( &mut self, upper_left: BackendCoord, bottom_right: BackendCoord, style: &S, fill: bool, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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
draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( &mut self, path: I, style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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
draw_circle<S: BackendStyle>( &mut self, center: BackendCoord, radius: u32, style: &S, fill: bool, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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
fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( &mut self, path: I, style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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
draw_text<S: BackendTextStyle>( &mut self, text: &str, style: &S, pos: BackendCoord, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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
270 impl Drop for MockedBackend {
drop(&mut self)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
create_mocked_drawing_area<F: FnOnce(&mut MockedBackend)>( width: u32, height: u32, setup: F, ) -> DrawingArea<MockedBackend, Shift>288 pub 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